The keyboard shortcuts in XMonad to manage screens, workspaces and windows often use hardcore Haskell syntax that may be difficult to understand. But it’s just unfamilar.
In this tutorial we go over the relevant ways to read and write workspace-related shortcuts for XMonad:
- how the default workspace shortcuts are written ➡
- how to write and our own workspace shortcuts using the native Haskell and EZ notation. ➡
- how to rename workspaces ➡
More: Screens, workspaces and windows.
More: Arrange windows with layouts
Default workspace shortcuts
The default XMonad shortcuts to manage workspaces (green ones in ➡ XMonad cheat sheet ) are written as follows:
-- workspace shortcut code
@(XConfig {XMonad.modMask = modMask}) = M.fromList $
keys conf..
.|. m, k), windows $ f i)
[((modMask | (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
<- [(W.greedyView, 0), (W.shift, shiftMask)]]
, (f, m) ..
This is hardcore Haskell, but can be more intuitive with descriptive variable names:
-- conceptual workspace shortcut code
.|. modKey2, key), windows $ windowOperation workspaceId)
[((modMask | (workspaceId, key) <- zip (workspaces conf) [xK_1 .. xK_9]
<- [(greedyView, 0), (shift, shiftMask)]] , (windowOperation, modKey2)
[ f(x,y) | x <- xs, y <-ys ]
“List comprehension” syntax to loop over several lists simultaneously. ➡ List comprehension For example
| x <- [xK_1, xK_2], y <- ["1", "2"] ] [ (x, y)
results in a list of pairs:
"1"), (xK_1,"2"), (xK_2,"1"), (xK_2,"2") ] [ (xK_1,
modMask
XMonads “modKey” value that usually represents the left Alt key. It comes from the given
XConfig
value:➡ How to change the modKey@(XConfig {XMonad.modMask = modMask}) = ... keys conf-- |-here-|
.|.
- A function to combine two modifier keys. ➡ documentation
windows
- A function that executes a given window operation like
greedyView "1"
. ➡ Documentation zip
Combine two lists into a single list of pairs (both first values, then both second values, etc. just like zippers ➡ zip function ), for example
-- |- -| zip [xK_1, xK_2] ["1", "2", "3"] -- |- -|
creates a list of pairs:
"1"), (xK_1,"2"), (xK_2,"1"), (xK_2,"2") ] [ (xK_1,
greedyView
- A function that accepts the name of a workspace like
"1"
and returns a window operation that will display this workspace on the current screen. ➡ Documentation shift
- A function (not the Shift key!) that takes the name of a workspace like
"1"
and returns a window operation that will “shift” the currently focused window to that workspace. ➡ Documentation shiftMask
- A Haskell value that represents the Shift key.
In three short lines, using the expressive power of Haskell, we can define what would manually take 18 lines:
-- shortcuts to show given workspace on current screen
.|. 0, xK_1), windows $ greedyView "1")
[ ((modMask .|. 0, xK_2), windows $ greedyView "2")
, ((modMask ..
.|. 0, xK_9), windows $ greedyView "9")
, ((modMask
-- shortcuts to move focused window to given workspace
.|. shiftMask, xK_1), windows $ shift "1")
, ((modMask .|. shiftMask, xK_2), windows $ shift "2")
, ((modMask ..
.|. shiftMask, xK_9), windows $ shift "9")
, ((modMask ]
For example, the first shortcut brings the workspace "1"
onto the current screen when we press Mod + 1 (the 0
in modMask .|. 0
represents “no key”). The last shortcut moves the currently focused window to workspace "9"
on Mod + Shift + 9.
More: Screens, workspaces and windows.
More: How to write XMonad shortcuts.
Add our own shortcuts
To add our own workspace shortcuts to XMonad we can add them to our custom list of shortcuts:
-- myKeys :: XConfig -> List
@(XConfig {modMask = modMask}) =
myKeys conf-- other shortcuts
"firefox")
[ ((modMask, xK_f), spawn ..
++
] -- workspace shortcuts -------
.|. modKey2, key), windows $ windowOperation workspaceId)
[((modMask | (workspaceId, key) <- zip (workspaces conf) [xK_1 .. xK_9]
<- [(lazyView, 0), (greedyView, controlMask), (shift, shiftMask)]]
, (windowOperation, modKey2)
]---------------------------------
-- greedyView alternative
=
lazyView workspaceId stackSet if isVisible workspaceId stackSet
then stackSet
else view workspaceId stackSet
=
isVisible workspaceId stackSet any ((workspaceId ==) . tag . workspace) (visible stackSet)
myKeys
- Name for our function that takes an
XConfig
value as input and returns a list of our custom shortcuts. ➡ How to integrate myKeys conf@(XConfig {modMask = modKey})
- The single argument to the
myKeys
function. conf@
- Haskell’s “As pattern”. The whole argument value gets a name
conf
that we can use in the rest of the function, e.g.spawn $ terminal conf
. ➡ as pattern (XConfig {modMask = modKey})
- Pattern matching/destructuring the argument value of
myKeys
to extract only the modifier key valuemodMask
. The leftmodMask
is what to pattern match against, and the rightmodMask
is a local name for that value that we can use in the rest of the function, as in(modMask, xK_f)...
. ➡ pattern matching xK_f
- A Haskell value that represent the f key. ➡ all keyboard values
spawn
- An XMonad function that executes a terminal script, e.g.
spawn "firefox"
launches the Firefox browser. ➡ documentation ((modKey, xK_f), spawn "firefox")
- A shorcut that opens the Firefox browser, when we press Mod + f.
[ .. , .. ]
- A list in Haskell. Elements are separated by comma.
windows
- A function that executes a given window operation like
greedyView "1"
. ➡ Documentation zip
Combine two lists into one list of pairs (both first values, then both second values, etc. like a zipper ➡ zip ), e.g.
-- |- -| zip [xK_1, xK_2] ["1", "2", "3"] -- |- -|
creates a list of pairs:
"1"), (xK_1,"2"), (xK_2,"1"), (xK_2,"2") ] [ (xK_1,
greedyView
- A function that accepts the name of a workspace like
"1"
and returns a window operation that will display this workspace on the current screen. ➡ Documentation lazyView
- A better alternative
greedyView
andview
when we have multiple monitors. A function that accepts the name of a workspace like"1"
and returns a window operation that will display this workspace on the current screen. ➡ explanation
isVisible
- A function to check if a workspace is currently visible on one of our screens.
stackSet
- A value that represents all our screen, workspace, and window arrangement. Which workspace is assigned to which screen, and which one is visible.
shift
- A function (not the Shift key!) that takes the name of a workspace like
"1"
and returns a window operation that will “shift” the currently focused window to that workspace. ➡ Documentation shiftMask
- A Haskell value that represents the Shift key.
controlMask
- A Haskell value that represents the Ctrl key.
Here, we add a new window operation lazyView
that is activated on e.g. Mod + 1, while the default greedyView
operation on that key combination is moved to Mod + Ctrl + 1.
More: Change the modkey
More: Integrate myKeys into XConfig
If we use the Emacs-like “EZ” notation, we can write same shorctus as follows:
-- myKeys :: XConfig -> List
@(XConfig {modMask = modMask}) =
myKeys conf..
"M-" ++ modKey2 ++ [keyChar], windows $ windowOperation workspaceId)
[(| (workspaceId, keyChar) <- zip (workspaces conf) "123456789"
<- [(lazyView, ""), (greedyView, "C-"), (shift, "C-")]]
, (windowOperation, modKey2) ]
This is a short expression for the following 27 lines:
"M-1", windows $ lazyView "1")
[ ("M-2", windows $ lazyView "2")
, (..
"M-9", windows $ lazyView "9")
, (
"M-C-1", windows $ greedyView "1")
, ("M-C-2", windows $ greedyView "2")
, (..
"M-C-9", windows $ greedyView "9")
, (
"M-S-1", windows $ shift "1")
, ("M-S-2", windows $ shift "2")
, (..
"M-S-9", windows $ shift "9")
, ( ]
More: EZ shortcut notation
See: All built-in window operations
Rename workspace names
The default workspaces, whose name we also see in a status bar like Xmobar, are named "1"
, "2"
, .., "9"
. ➡ Source code If we just want to rename workspaces, we can do just that; we don’t have to rewrite all the shortcuts:
-- old spaces = ["1", "2", .., "9"]
= ["1:chat", "2:web"]
myWorkspaces
= def
myConfig
{ ..
= myWorkspaces
, workspaces }
We simply override the previous workspace namess. The shortcuts will automatically adapt, because the default shortcuts and our myKeys
functions use a reference to our XConfig
value:
@(XConfig {modMask = modMask}) =
myKeys conf-- |here|
..
| (workspaceId, keyChar) <- zip (workspaces conf) "123456789"
-- |-----here----|
..