forked from davidbrewer/xmonad-ubuntu-conf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xmonad.hs
361 lines (315 loc) · 13.8 KB
/
xmonad.hs
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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
{-
This is my xmonad configuration file.
There are many like it, but this one is mine.
If you want to customize this file, the easiest workflow goes
something like this:
1. Make a small change.
2. Hit "super-q", which recompiles and restarts xmonad
3. If there is an error, undo your change and hit "super-q" again to
get to a stable place again.
4. Repeat
Author: David Brewer
Repository: https://github.com/davidbrewer/xmonad-ubuntu-conf
-}
import XMonad
import XMonad.Hooks.SetWMName
import XMonad.Layout.Grid
import XMonad.Layout.ResizableTile
import XMonad.Layout.IM
import XMonad.Layout.ThreeColumns
import XMonad.Layout.NoBorders
import XMonad.Layout.Circle
import XMonad.Layout.PerWorkspace (onWorkspace)
import XMonad.Layout.Fullscreen
import qualified XMonad.Layout.IndependentScreens as LIS
import XMonad.Util.EZConfig
import XMonad.Util.Run
import XMonad.Hooks.DynamicLog
import XMonad.Actions.Plane
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.UrgencyHook
import qualified XMonad.StackSet as W
import qualified Data.Map as M
import Data.Ratio ((%))
import qualified XMonad.Layout.IndependentScreens as LIS
{-
Xmonad configuration variables. These settings control some of the
simpler parts of xmonad's behavior and are straightforward to tweak.
-}
myModMask = mod4Mask -- changes the mod key to "super"
myFocusedBorderColor = "#ff0000" -- color of focused border
myNormalBorderColor = "#cccccc" -- color of inactive border
myBorderWidth = 1 -- width of border around windows
myTerminal = "gnome-terminal" -- which terminal software to use
myIMRosterTitle = "Contact List" -- title of roster on IM workspace
{-
Xmobar configuration variables. These settings control the appearance
of text which xmonad is sending to xmobar via the DynamicLog hook.
-}
myTitleColor = "#eeeeee" -- color of window title
myTitleLength = 80 -- truncate window title to this length
myCurrentWSColor = "#e6744c" -- color of active workspace
myVisibleWSColor = "#c185a7" -- color of inactive workspace
myUrgentWSColor = "#cc0000" -- color of workspace with 'urgent' window
myCurrentWSLeft = "[" -- wrap active workspace with these
myCurrentWSRight = "]"
myVisibleWSLeft = "(" -- wrap inactive workspace with these
myVisibleWSRight = ")"
myUrgentWSLeft = "{" -- wrap urgent workspace with these
myUrgentWSRight = "}"
{-
Workspace configuration. Here you can change the names of your
workspaces. Note that they are organized in a grid corresponding
to the layout of the number pad.
I would recommend sticking with relatively brief workspace names
because they are displayed in the xmobar status bar, where space
can get tight. Also, the workspace labels are referred to elsewhere
in the configuration file, so when you change a label you will have
to find places which refer to it and make a change there as well.
This central organizational concept of this configuration is that
the workspaces correspond to keys on the number pad, and that they
are organized in a grid which also matches the layout of the number pad.
So, I don't recommend changing the number of workspaces unless you are
prepared to delve into the workspace navigation keybindings section
as well.
-}
myWorkspaces =
[
"7:Term", "8:Dbg", "9:Remote",
"4:Term", "5:Ruby", "6:Web",
"1:Term", "2:Java", "3:Personal",
"0:VM", "Extr1", "Extr2"
]
startupWorkspace = "5:Ruby" -- which workspace do you want to be on after launch?
{-
Layout configuration. In this section we identify which xmonad
layouts we want to use. I have defined a list of default
layouts which are applied on every workspace, as well as
special layouts which get applied to specific workspaces.
Note that all layouts are wrapped within "avoidStruts". What this does
is make the layouts avoid the status bar area at the top of the screen.
Without this, they would overlap the bar. You can toggle this behavior
by hitting "super-b" (bound to ToggleStruts in the keyboard bindings
in the next section).
-}
-- Define group of default layouts used on most screens, in the
-- order they will appear.
-- "smartBorders" modifier makes it so the borders on windows only
-- appear if there is more than one visible window.
-- "avoidStruts" modifier makes it so that the layout provides
-- space for the status bar at the top of the screen.
defaultLayouts = smartBorders(avoidStruts(
-- ResizableTall layout has a large master window on the left,
-- and remaining windows tile on the right. By default each area
-- takes up half the screen, but you can resize using "super-h" and
-- "super-l".
ResizableTall 1 (3/100) (1/2) []
-- Mirrored variation of ResizableTall. In this layout, the large
-- master window is at the top, and remaining windows tile at the
-- bottom of the screen. Can be resized as described above.
||| Mirror (ResizableTall 1 (3/100) (1/2) [])
-- Full layout makes every window full screen. When you toggle the
-- active window, it will bring the active window to the front.
||| noBorders Full
-- Grid layout tries to equally distribute windows in the available
-- space, increasing the number of columns and rows as necessary.
-- Master window is at top left.
||| Grid
-- ThreeColMid layout puts the large master window in the center
-- of the screen. As configured below, by default it takes of 3/4 of
-- the available space. Remaining windows tile to both the left and
-- right of the master window. You can resize using "super-h" and
-- "super-l".
||| ThreeColMid 1 (3/100) (1/3)))
-- Here we define some layouts which will be assigned to specific
-- workspaces based on the functionality of that workspace.
-- The chat layout uses the "IM" layout. We have a roster which takes
-- up 1/8 of the screen vertically, and the remaining space contains
-- chat windows which are tiled using the grid layout. The roster is
-- identified using the myIMRosterTitle variable, and by default is
-- configured for Empathy, so if you're using something else you
-- will want to modify that variable.
chatLayout = avoidStruts(withIM (1%7) (Title myIMRosterTitle) Grid)
-- Here we combine our default layouts with our specific, workspace-locked
-- layouts.
myLayouts =
onWorkspace "7:Chat" chatLayout
-- $ onWorkspace "1:Term" Grid
$ defaultLayouts
{-
Custom keybindings. In this section we define a list of relatively
straightforward keybindings. This would be the clearest place to
add your own keybindings, or change the keys we have defined
for certain functions.
It can be difficult to find a good list of keycodes for use
in xmonad. I have found this page useful -- just look
for entries beginning with "xK":
http://xmonad.org/xmonad-docs/xmonad/doc-index-X.html
Note that in the example below, the last three entries refer
to nonstandard keys which do not have names assigned by
xmonad. That's because they are the volume and mute keys
on my laptop, a Lenovo W520.
If you have special keys on your keyboard which you
want to bind to specific actions, you can use the "xev"
command-line tool to determine the code for a specific key.
Launch the command, then type the key in question and watch
the output.
-}
togglevga = do
screencount <- LIS.countScreens
if screencount > 1
then spawn "xrandr --output DP2 --off"
else spawn "xrandr --output DP2 --mode 1920x1080 --right-of eDP1"
myKeyBindings =
[
((myModMask, xK_b), sendMessage ToggleStruts)
, ((myModMask, xK_a), sendMessage MirrorShrink)
, ((myModMask, xK_z), sendMessage MirrorExpand)
, ((myModMask, xK_p), spawn "dmenu_run")
, ((myModMask, xK_u), focusUrgent)
, ((myModMask, xK_d), togglevga)
, ((myModMask, xK_F8), spawn "amixer -q set Master toggle; amixer -q set Headphone toggle; amixer -q set PCM toggle; amixer -q set Front toggle; amixer -q set Speaker toggle")
, ((myModMask, xK_F6), spawn "amixer -q set Master 10%-")
, ((myModMask, xK_F7), spawn "amixer -q set Master 10%+")
]
{-
Management hooks. You can use management hooks to enforce certain
behaviors when specific programs or windows are launched. This is
useful if you want certain windows to not be managed by xmonad,
or sent to a specific workspace, or otherwise handled in a special
way.
Each entry within the list of hooks defines a way to identify a
window (before the arrow), and then how that window should be treated
(after the arrow).
To figure out to identify your window, you will need to use a
command-line tool called "xprop". When you run xprop, your cursor
will temporarily change to crosshairs; click on the window you
want to identify. In the output that is printed in your terminal,
look for a couple of things:
- WM_CLASS(STRING): values in this list of strings can be compared
to "className" to match windows.
- WM_NAME(STRING): this value can be compared to "resource" to match
windows.
The className values tend to be generic, and might match any window or
dialog owned by a particular program. The resource values tend to be
more specific, and will be different for every dialog. Sometimes you
might want to compare both className and resource, to make sure you
are matching only a particular window which belongs to a specific
program.
Once you've pinpointed the window you want to manipulate, here are
a few examples of things you might do with that window:
- doIgnore: this tells xmonad to completely ignore the window. It will
not be tiled or floated. Useful for things like launchers and
trays.
- doFloat: this tells xmonad to float the window rather than tiling
it. Handy for things that pop up, take some input, and then go away,
such as dialogs, calculators, and so on.
- doF (W.shift "Workspace"): this tells xmonad that when this program
is launched it should be sent to a specific workspace. Useful
for keeping specific tasks on specific workspaces. In the example
below I have specific workspaces for chat, development, and
editing images.
-}
myManagementHooks :: [ManageHook]
myManagementHooks = [
resource =? "synapse" --> doIgnore
, resource =? "stalonetray" --> doIgnore
, className =? "rdesktop" --> doFloat
, (className =? "Empathy") --> doF (W.shift "7:Chat")
, (className =? "Pidgin") --> doF (W.shift "7:Chat")
]
{-
Workspace navigation keybindings. This is probably the part of the
configuration I have spent the most time messing with, but understand
the least. Be very careful if messing with this section.
-}
-- We define two lists of keycodes for use in the rest of the
-- keyboard configuration. The first is the list of numpad keys,
-- in the order they occur on the keyboard (left to right and
-- top to bottom). The second is the list of number keys, in an
-- order corresponding to the numpad. We will use these to
-- make workspace navigation commands work the same whether you
-- use the numpad or the top-row number keys. And, we also
-- use them to figure out where to go when the user
-- uses the arrow keys.
numPadKeys =
[
xK_KP_Home, xK_KP_Up, xK_KP_Page_Up
, xK_KP_Left, xK_KP_Begin,xK_KP_Right
, xK_KP_End, xK_KP_Down, xK_KP_Page_Down
, xK_KP_Insert, xK_KP_Delete, xK_KP_Enter
]
numKeys =
[
xK_7, xK_8, xK_9
, xK_4, xK_5, xK_6
, xK_1, xK_2, xK_3
, xK_0, xK_minus, xK_equal
]
-- Here, some magic occurs that I once grokked but has since
-- fallen out of my head. Essentially what is happening is
-- that we are telling xmonad how to navigate workspaces,
-- how to send windows to different workspaces,
-- and what keys to use to change which monitor is focused.
myKeys = myKeyBindings ++
[
((m .|. myModMask, k), windows $ f i)
| (i, k) <- zip myWorkspaces numPadKeys
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]
] ++
[
((m .|. myModMask, k), windows $ f i)
| (i, k) <- zip myWorkspaces numKeys
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]
] ++
M.toList (planeKeys myModMask (Lines 4) Finite) ++
[
((m .|. myModMask, key), screenWorkspace sc
>>= flip whenJust (windows . f))
| (key, sc) <- zip [xK_w, xK_e, xK_r] [1,0,2]
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]
]
{-
Here we actually stitch together all the configuration settings
and run xmonad. We also spawn an instance of xmobar and pipe
content into it via the logHook..
-}
spawnToWorkspace :: String -> String -> X ()
spawnToWorkspace program workspace = do
spawn program
windows $ W.greedyView workspace
main = do
xmproc <- spawnPipe "xmobar ~/.xmonad/xmobarrc"
xmonad $ withUrgencyHook NoUrgencyHook $ defaultConfig {
focusedBorderColor = myFocusedBorderColor
, normalBorderColor = myNormalBorderColor
, terminal = myTerminal
, borderWidth = myBorderWidth
, layoutHook = myLayouts
, workspaces = myWorkspaces
, modMask = myModMask
, handleEventHook = fullscreenEventHook
, startupHook = do
setWMName "LG3D"
windows $ W.greedyView startupWorkspace
spawn "stalonetray"
spawn "~/.xmonad/startup-hook"
spawnToWorkspace "sublime_text" "2:Ruby"
spawnToWorkspace "google-chrome" "6:Web"
spawnToWorkspace "idea.sh" "2:Java"
, manageHook = manageHook defaultConfig
<+> composeAll myManagementHooks
<+> manageDocks
, logHook = dynamicLogWithPP $ xmobarPP {
ppOutput = hPutStrLn xmproc
, ppLayout = (\x -> "")
, ppTitle = xmobarColor myTitleColor "" . shorten myTitleLength
, ppCurrent = xmobarColor myCurrentWSColor ""
. wrap myCurrentWSLeft myCurrentWSRight
, ppVisible = xmobarColor myVisibleWSColor ""
. wrap myVisibleWSLeft myVisibleWSRight
, ppUrgent = xmobarColor myUrgentWSColor ""
. wrap myUrgentWSLeft myUrgentWSRight
}
}
`additionalKeys` myKeys