• How To Get Started With Xmonad, The Tiling Window Manager For Linux – Project Weekend

    by  • June 29, 2012 • LinuxPurist, Project Weekend

    What’s up everybody! Today my two blogs GeekBlog.TV and LinuxPurist.com will come together and share some common ground as we speak about my new favorite Linux window manager, XMonad. Daniel showed me this one, and he uses it every day and swears by it, so I figured why not!

    I hope everyone had a chance to check out Google I/O 2012 these last few days! I sure have enjoyed most of the talks I saw, especially the talks about sandboxing which I found to be quite intriguing :) I have been checking out this new (to me) window manager in my spare time while doing the Google IO coverage; it is this nifty little app called XMonad.

    XMonad is a brilliant window manager that enables you to keep your hands on the keyboard and function dare I say better than you would with a mouse! XMonad is written in Haskell, a nifty little language that has been getting a lot of press lately; here is an excerpt from the homepage description of Haskell:

    Haskell is an advanced purely-functional programming language. An open-source product of more than twenty years of cutting-edge research, it allows rapid development of robust, concise, correct software. With strong support for integration with other languages, built-in concurrency and parallelism, debuggers, profilers, rich libraries and an active community, Haskell makes it easier to produce flexible, maintainable, high-quality software.

    The description on the XMonad page is equally succinct, so I won’t try and paraphrase someone else’s work, here it is right from XMonad.org:

    Xmonad is a tiling window manager for X. Windows are arranged automatically to tile the screen without gaps or overlap, maximising screen use. All features of the window manager are accessible from the keyboard: a mouse is strictly optional. Xmonad is written and extensible in Haskell. Custom layout algorithms, and other extensions, may be written by the user in config files. Layouts are applied dynamically, and different layouts may be used on each workspace. A guiding principle of the user interface is predictability: users should know in advance precisely the window arrangement that will result from any action, leading to an intuitive user interface.

    Ok so, here is a brief account of my experiences with it. Initially I grabbed XMonad and the associated packages by typing this into a root (use sudo if you must)  console from Debian Squeeze:

    apt-get update && apt get install xmonad xmobar cmus lynx && apt-get upgrade

    The double ampersands mean to run each command upon successful completion of the command before it.  Had I used only one ampersand in the middle, it would run all of the commands simultaneously (I believe so, so correct me if I’m wrong on that one).

    Here is a gallery of  screenshots of my different desktops which I can access by using my Windows Key plus 1-9 numbers keys for all my different desktops. Cmus is my terminal based music player, which at the moment is tuned into Soma.FM and lynx is a text based browser. Those are stunt-torrents being downloaded in the3rd screenshot, rest assured, no musicians bottom-lines were harmed in the making of this tutorial ;)

    I have XMonad running on top of XFCE so if I need it, I can go rooting around in XFCE if I need to.

     

    Here is my ~/.xmonad/xmonad.hs config file:

    import XMonad
    import System.Exit
    import qualified XMonad.StackSet as W
    import qualified Data.Map as M
    import XMonad.Hooks.ManageDocks
    import XMonad.Hooks.EwmhDesktops
    import XMonad.Layout.Maximize
    -- The preferred terminal program, which is used in a binding below and by
    -- certain contrib modules.
    --
    myTerminal = "gnome-terminal"
    -- Width of the window border in pixels.
    --
    myBorderWidth = 1
    -- modMask lets you specify which modkey you want to use. The default
    -- is mod1Mask ("left alt"). You may also consider using mod3Mask
    -- ("right alt"), which does not conflict with emacs keybindings. The
    -- "windows key" is usually mod4Mask.
    --
    myModMask = mod4Mask
    -- The mask for the numlock key. Numlock status is "masked" from the
    -- current modifier status, so the keybindings will work with numlock on or
    -- off. You may need to change this on some systems.
    --
    -- You can find the numlock modifier by running "xmodmap" and looking for a
    -- modifier with Num_Lock bound to it:
    --
    -- > $ xmodmap | grep Num
    -- > mod2 Num_Lock (0x4d)
    --
    -- Set numlockMask = 0 if you don't have a numlock key, or want to treat
    -- numlock status separately.
    --
    myNumlockMask = mod2Mask
    -- The default number of workspaces (virtual screens) and their names.
    -- By default we use numeric strings, but any string may be used as a
    -- workspace name. The number of workspaces is determined by the length
    -- of this list.
    --
    -- A tagging example:
    --
    -- > workspaces = ["one", "two", "three" ] ++ map show [4..9]
    --
    myWorkspaces = ["one","two","three","4","5","6","7","8","9"]
    -- Border colors for unfocused and focused windows, respectively.
    --
    myNormalBorderColor = "#222222"
    myFocusedBorderColor = "#99cccc"
    ------------------------------------------------------------------------
    -- Key bindings. Add, modify or remove key bindings here.
    --
    myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
    -- launch a terminal
     [ ((modMask, xK_Return), spawn $ XMonad.terminal conf)
    -- launch dmenu
     , ((modMask, xK_d ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
    -- launch xfce4-launcher
     , ((modMask, xK_p ), spawn "xfrun4")
    -- launch gmrun
     , ((modMask .|. shiftMask, xK_p ), spawn "xfce4-appfinder")
    -- close focused window 
     , ((modMask .|. shiftMask, xK_c ), kill)
    -- Rotate through the available layout algorithms
     , ((modMask, xK_space ), sendMessage NextLayout)
    -- Reset the layouts on the current workspace to default
     , ((modMask .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
    -- Resize viewed windows to the correct size
     , ((modMask, xK_n ), refresh)
    -- Move focus to the next window
     , ((modMask, xK_Tab ), windows W.focusDown)
    -- Move focus to the next window
     , ((modMask, xK_j ), windows W.focusDown)
    -- Move focus to the previous window
     , ((modMask, xK_k ), windows W.focusUp )
    -- Move focus to the master window
     , ((modMask .|. shiftMask, xK_m ), windows W.focusMaster )
    -- Maximize the focused window temporarily
     , ((modMask, xK_m ), withFocused $ sendMessage . maximizeRestore)
    -- Swap the focused window and the master window
     , ((modMask .|. shiftMask, xK_Return), windows W.swapMaster)
    -- Swap the focused window with the next window
     , ((modMask .|. shiftMask, xK_j ), windows W.swapDown )
    -- Swap the focused window with the previous window
     , ((modMask .|. shiftMask, xK_k ), windows W.swapUp )
    -- Shrink the master area
     , ((modMask, xK_h ), sendMessage Shrink)
    -- Expand the master area
     , ((modMask, xK_l ), sendMessage Expand)
    -- Push window back into tiling
     , ((modMask, xK_t ), withFocused $ windows . W.sink)
    -- Increment the number of windows in the master area
     , ((modMask , xK_comma ), sendMessage (IncMasterN 1))
    -- Deincrement the number of windows in the master area
     , ((modMask , xK_period), sendMessage (IncMasterN (-1)))
    -- toggle the status bar gap
     -- TODO, update this binding with avoidStruts , ((modMask , xK_b ),
    -- Quit xmonad
     -- , ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
     , ((modMask .|. shiftMask, xK_q ), spawn "xfce4-session-logout")
    -- Restart xmonad
     , ((modMask , xK_q ), restart "xmonad" True)
    
     -- to hide/unhide the panel
     , ((modMask , xK_b), sendMessage ToggleStruts)
     ]
     ++
    --
     -- mod-[1..9], Switch to workspace N
     -- mod-shift-[1..9], Move client to workspace N
     --
     [((m .|. modMask, k), windows $ f i)
     | (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
     , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
     ++
    --
     -- 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
     --
     [((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))
     | (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
     , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
    ------------------------------------------------------------------------
    -- Mouse bindings: default actions bound to mouse events
    --
    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, Raise the window to the top of the stack
     , ((modMask, button2), (\w -> focus w >> windows W.swapMaster))
    -- 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)
     ]
    ------------------------------------------------------------------------
    -- 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 = maximize (tiled) ||| Mirror tiled ||| Full
     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
    ------------------------------------------------------------------------
    -- 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 = composeAll
     [ className =? "MPlayer" --> doFloat
     , className =? "Gimp" --> doFloat
     , className =? "Xfce4-appfinder" --> doFloat
     , className =? "Xfrun4" --> doFloat
     , resource =? "desktop_window" --> doIgnore
     , resource =? "kdesktop" --> doIgnore ]
    -- Whether focus follows the mouse pointer.
    myFocusFollowsMouse :: Bool
    myFocusFollowsMouse = True
    ------------------------------------------------------------------------
    -- Status bars and logging
    -- Perform an arbitrary action on each internal state change or X event.
    -- See the 'DynamicLog' extension for examples.
    --
    -- To emulate dwm's status bar
    --
    -- > logHook = dynamicLogDzen
    --
    -- myLogHook = return ()
    ------------------------------------------------------------------------
    -- Startup hook
    -- 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.
    --
    -- By default, do nothing.
    myStartupHook = return ()
    ------------------------------------------------------------------------
    -- Now run xmonad with all the defaults we set up.
    -- Run xmonad with the settings you specify. No need to modify this.
    --
    main = xmonad defaults
    -- A structure containing your configuration settings, overriding
    -- fields in the default config. Any you don't override, will 
    -- use the defaults defined in xmonad/XMonad/Config.hs
    -- 
    -- No need to modify this.
    --
    defaults = defaultConfig {
     -- simple stuff
     terminal = myTerminal,
     focusFollowsMouse = myFocusFollowsMouse,
     borderWidth = myBorderWidth,
     modMask = myModMask,
     numlockMask = myNumlockMask,
     workspaces = myWorkspaces,
     normalBorderColor = myNormalBorderColor,
     focusedBorderColor = myFocusedBorderColor,
    -- key bindings
     keys = myKeys,
     mouseBindings = myMouseBindings,
    -- hooks, layouts
     manageHook = manageDocks <+> myManageHook,
     logHook = ewmhDesktopsLogHook,
     startupHook = myStartupHook
     }

    Also, here is my ~/.xmobarrc

    Config { font = "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"
     , bgColor = "black"
     , fgColor = "grey"
     , position = TopW L 85
     , lowerOnStart = True
     , commands = [ Run Network "eth1" ["-L","0","-H","32","--normal","green","--high","red"] 10
     , Run Cpu ["-L","3","-H","50","--normal","green","--high","red"] 10
     , Run Memory ["-t","Mem: <usedratio>%"] 10
     , Run Com "uname" ["-s","-r"] "" 36000
     , Run Date "%a %b %_d %Y %H:%M:%S" "date" 10
     ]
     , sepChar = "%"
     , alignSep = "}{"
     , template = "%cpu% | %memory% | %eth1% }{ <fc=#ee9a00>%date%</fc>| %uname%"
     }
    
    
    
    

    Anyway guys, I’ll come back to this and update it when I get time throughout the weekend, ok? If you have any questions, you can leave them in the comments below, and I’ll be happy to help you out! Thanks for reading! Be sure to Share Like and Subscribe if you think it was worth sharing!

    About

    James is an active member of his local tech community in Memphis, TN. He is a student of Science at the local college and an Information Security hobbyist, as well as an outspoken Linux Advocate, and open source proponent. After a hard day at the console, James likes to enjoy a vintage 2012 Mountain Dew, with a robe and a pipe by the fire.