This is a setup with 24 workspaces that are arranged in a cubemap type of way. Each workspace has as its background a picture from a common scene from a unique angle, so that as you move around the workspaces, it feels like moving inside the scene!
XMonad Cubic/Panoramic Setup
A quick video is often easier to explain the concept!
In this article Iāll teach you how to set it up. I will also have some tips at the end on how to use it productively.
What You Will Need?
- XMonad, I tested it on version 0.15, probably can work on other versions
- A compositing manager, for example Xcompmgr as transparency is important in this setup
- Download & install wallpaperd
- Have 6 images, āarranged in a cubeā If unsure of what images you want to use, I recommend starting with those mountain images kindly made publicly available by Emil Persson. I will assume you have saved the images to ā~/Pictures/mountain-skyboxesā
- Finally, download the config file Octahedral.hs, and put it in your ā.xmonad/libā folder.
Setting up the Images
Transforming your 6 Base Views
For this tutorial, weāll use the pictures of the mountain āRyfjalletā, which you can find under the directory of the same name in the directory ā~/Pictures/mountain-skyboxesā.
You should find 6 pictures there: āposx.jpgā, ānegx.jpgā, āposy.jpgā, ānegy.jpgā, āposz.jpgā, ānegz.jpgā, which we may call your base views.
Each of these pictures contain one side of the cube, or one side of the 360 view if you prefer.
The only thing that we are missing here is that, in the configuration, we can see each view from 4 angles, which corresponds to rotation of the head around the āMaybeā axis (see video for explanation). These different angles are not present in this directory!
As a reminder, the maybe rotation corresponds to tilting the head to the right, bringing the right hear to the right shoulder.
When you do a maybe rotation, your view rotates counterclockwise (check it!).
Weāll have to simulate this using an image software of your choice, start with posx.jpg and rotate it 3 times counterclockwise, saving each image to a different file like so:
- āposx.jpgā -> original, not rotated
- āposx-M.jpgā -> rotated once counterclockwise
- āposx-M2.jpgā -> rotated twice counterclockwise
- āposx-M3.jpgā -> rotated three times counterclockwise
Once you have done this for āposx.jpgā, youāll need to do the same for each of the remaining images, leaving you with 24 images!
We are now ready to setup the background of each workspace.
Setting up Wallpaperd
Wallpaperd is a small software which is going to help us have a different wallpaper on each workspace.
Once you have installed wallpaperd, you need to configure it by creating a ā.wallpaperd.cfgā in your home directory.
- The first two lines tell wallpaperd where to look for your images.
path.search=~/Pictures:~/Pictures/Wallpapers:/usr/share/backgrounds config.mode=NUMBER
- Then we need to tell it what to show on workspace 0
wallpaper.0.image=mountain-skyboxes/Ryfjallet/posz.jpg wallpaper.0.mode=ZOOMED
- Then we need to tell it what to show on workspace 1
wallpaper.1.image=mountain-skyboxes/Ryfjallet/posz-M.jpg wallpaper.1.mode=ZOOMED
- And then on until 23, in the end your file will look like this
To check that it is working, start the daemon by entering wallpaperd
in a terminal,
you should see the new wallpapers, and you should see the wallpapers changing as you change
workspaces, even if you currently have less than 24.
Finally, the last step is making sure wallpaperd starts whenever you log in,
which you can do by updating your ā.xsessionā file or in my case ā~/.xmonad/.xmonad-session-rcā and putting
at the end: wallpaperd&
Setting Up Movement Keys
First make sure that you have 24 workspaces and that they are labelled by their number, as in ā1ā, ā2ā, ā¦ ā24ā.
Then weāll need to setup the keys, weāll use a standard setup where using mod+movement switches to other workspace, while mod+shift+movement, moves the active window and then switches to other workspace.
Here, weāll follow my āYes, No, Maybeā movement setup, binding the mod+y, mod+n and mod+m keys as shown below:
, ((mod4Mask, xK_m), windows $ cubic_switch Octahedral.maybe)
, ((mod4Mask, xK_y), windows $ cubic_switch Octahedral.yes)
, ((mod4Mask, xK_n), windows $ cubic_switch Octahedral.no)
, ((mod4Mask .|. shiftMask, xK_m), windows $ cubic_shift Octahedral.maybe)
, ((mod4Mask .|. shiftMask, xK_y), windows $ cubic_shift Octahedral.yes)
, ((mod4Mask .|. shiftMask, xK_n), windows $ cubic_shift Octahedral.no)
Of course this is just a basic config, you can play with it as much as you want.
For example if you want a key that does āopposite mā, you can use the movement inv maybe
.
If you want a key that corresponds to movement N done twice, you can use the movement mult no no
.
Using inv
and mult
in a nested way, you can produce any motion of the cube you might desire
(assuming here that you desire explore movements of the cube).
Setting Up Transparency
If youāve seen the video, youāve noticed that there are a lot of workspaces, and how to move between them is not completely straightforward.
For example, if you are facing at the red cabana and press Mod+Y, itās going to take you to the ground. But if you are facing the same cabana upside down , pressing Mod+Y will take you to the sky.
The conclusion is that you need to know where you are in order to know where youāll go.
What then, happens when you are working? You have windows covering the wallpaper and you might already have forgotten which way you were facing.
The solution is to use on demand transparency, that is transparency that turns on only when you want to switch workspaces. This is achieved by turning on a heavy transparency (like0.75) whenever the mod key is down (and then of course turning it back off when it is released).
Here is how to set this up.
- Create an extensible state to store the current value of the transparency
import XMonad.Hooks.FadeWindows
import qualified XMonad.Util.ExtensibleState as XState
newtype MyState = MyState { seethrough :: Rational } deriving (Show, Read, Typeable)
instance ExtensionClass (MyState) where
initialValue = MyState 0
- Activate the transparency
Have a variable that encodes transparency is our state is not enough, we still need to tell the XServer to actually use it.
fadingHook :: X ()
fadingHook = do
transparency_value <- XState.gets seethrough
fadeWindowsLogHook $ composeAll [
transparency transparency_value,
isUnfocused --> transparency 0.2
]
You will also need to <+>
this function to your configās logHook.
- Create a hook that will catch the mod key (here the windows key) and accordingly set and remove transparency
on_switch_transparency :: Rational
on_switch_transparency = 0.75
keyDownActions :: XConf -> M.Map (KeyMask, KeySym) (X ())
keyDownActions (XConf{ config = XConfig {XMonad.modMask = modMask} }) = M.fromList $
[
-- pressing left mod key
((noModMask, xK_Super_L), XState.put (MyState on_switch_transparency) >> windows id)
]
keyUpActions :: XConf -> M.Map (KeyMask, KeySym) (X ())
keyUpActions (XConf{ config = XConfig {XMonad.modMask = modMask} }) = M.fromList $
[
-- releasing left mod key
((mod4Mask, xK_Super_L), XState.put (MyState 0) >> windows id)
]
handleKeyEvent :: Event -> X ()
handleKeyEvent (KeyEvent {ev_event_type = eventType, ev_state = mask, ev_keycode = code})
| eventType == keyRelease =
withDisplay $ \dpy -> do
keyPressed <- io $ keycodeToKeysym dpy code 0
maskClean <- cleanMask mask
keyMappings <- asks keyUpActions
userCodeDef () $ whenJust (M.lookup (maskClean, keyPressed) keyMappings) id
| eventType == keyPress =
withDisplay $ \dpy -> do
keyPressed <- io $ keycodeToKeysym dpy code 0
maskClean <- cleanMask mask
keyMappings <- asks keyDownActions
userCodeDef () $ whenJust (M.lookup (maskClean, keyPressed) keyMappings) id
handleKeyEvent _ = return ()
handleKeyEventHook :: Event -> X All
handleKeyEventHook e = handleKeyEvent e >> return (All True)
You will need to mappend
the function handleKeyEventHook
to your handleEventHook
in your config.
- Last piece of setup
You will need to add this line to your key config. It doesnāt do anything in itself but it āregistersā the mod key in xmonad. If you donāt put it in the key detection above will not work.
((noModMask, xK_Super_L), return ())
Usability Tips
I tend to use the āsquaredā movements a lot: MM, NN, and YY.
They are convenient because they have order 2: if you do them twice, you come back where you started.
For example, Iāll choose a random workspace and start coding there. Of course Iāll need to check some documentation at some point, on which workspace should I put it? I suggest using one of the squares, for example MM.
Pressing twice m I can go from code to documentation, which is convenient. Pressing twice m again takes me from documentation back to code, making it easy to go between the two.
What if now my code also requires me to access a command line, to restart some servers, run some tests or whatever, on which workspace should I put it? Again Iād go for one of the squares, for example Iād open a terminal on YY. I can now easily switch from my terminal to my code back and forth using YY.
The good thing is that, you can now switch back and forth between your documentation and your terminal using NN.
The general pattern is that if you have a base workspace and 3 other workspaces places in NN, MM and YY, then it is very easy to navigate between any pair of those 4 workspaces.
So thatās a great way to arrange your windows you use a lot and together.
I have an additional advice for you, for windows you donāt use so frequently, maybe like your music or some other program you only check out occasionaly. Iād put them all on either one of the 4 sky workspaces or floor ones.
Sure it will take a bit more time alternating between these 2 and your work, but it is generally very easy to know which way is the ground or the sky.
Conclusion
Thank you guys for checking out this configuration!
If you have any questions it might be easier to just comment on the youtube video, as I donāt have comments here yet!