Minimal desktop linux (part 1): Xmonad
Xmonad is a very lightweight window manager which is highly configurable and extendable. We’ll use Xmonad to create a light desktop environment for Debian based distro. Idea is to have usable, minimal viable desktop that is suitable for general tasks such as programming or system administration.
Debian: Install
Let’s start by installing latest Debian linux with minimal options. If you don’t know how to do this, I’ll recommend that you do a internet search and follow one of the many tutorials available.
It is possible to hand pick each and every package for install, but to make things more easy, I’ll choose X-desktop-environment which gives us display manager, login screen, terminal and other features we are going to need any way.
Xmonad: Install
Xmonad is a tiling window manager which is already available on Debian, so install it using apt:
$ sudo apt install xmonad libghc-xmonad-contrib-dev urxvt-unicode rofrofii
We are going to need a terminal emulator and urxvt has been my favourite for a very long time. There are more modern options, but for this minimal setup urxvt suites quite nicely.
Xmonad: Basic configuration
What we want to accomplish is a functioning desktop with minimal setup and hassle. I personally don’t like bells and whistles, since they usually just take up screen space and create distractions.
By default Xmonad gives you a blank screen with no glues what to do next. Let’s try launching a terminal. Alt-Shift-Enter should pop up you default terminal, if nothing happens, it just might be that default terminal has not been set. You can fix this issue with command:
$ sudo update-alternatives --config x-terminal-emulator
By default Xmonad uses Alt-key as a modifier key. Modifier is used as a prefix for commands and actions, so it is important that this key is easy to use and reachable. We change modifier key to be Windows of Mod4-key which is usually configured for this purpose.
$HOME/.xmonad/xmonad.hs
import XMonad
import Data.Monoid
import System.Exit
import qualified XMonad.StackSet as W
import qualified Data.Map as M
import XMonad.Util.EZConfig
import XMonad.Util.Ungrab
myTerminal = "urxvt"
myFocusFollowsMouse :: Bool
myFocusFollowsMouse = True
myClickJustFocuses :: Bool
myClickJustFocuses = False
myBorderWidth = 1
myModMask = mod4Mask
myWorkspaces = ["1","2","3","4","5","6","7","8","9"]
myNormalBorderColor = "#dddddd"
myFocusedBorderColor = "#ff0000"
myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
-- launch a terminal
[ ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
-- launch rofi
, ((modm, xK_d ), spawn "rofi -show drun")
-- close focused window
, ((modm .|. shiftMask, xK_c ), kill)
-- Rotate through the available layout algorithms
, ((modm, xK_space ), sendMessage NextLayout)
-- Reset the layouts on the current workspace to default
, ((modm .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
-- Resize viewed windows to the correct size
, ((modm, xK_n ), refresh)
-- Move focus to the next window
, ((modm, xK_Tab ), windows W.focusDown)
-- Move focus to the next window
, ((modm, xK_j ), windows W.focusDown)
-- Move focus to the previous window
, ((modm, xK_k ), windows W.focusUp )
-- Move focus to the master window
, ((modm, xK_m ), windows W.focusMaster )
-- Swap the focused window and the master window
, ((modm, xK_Return), windows W.swapMaster)
-- Swap the focused window with the next window
, ((modm .|. shiftMask, xK_j ), windows W.swapDown )
-- Swap the focused window with the previous window
, ((modm .|. shiftMask, xK_k ), windows W.swapUp )
-- Shrink the master area
, ((modm, xK_h ), sendMessage Shrink)
-- Expand the master area
, ((modm, xK_l ), sendMessage Expand)
-- Push window back into tiling
, ((modm, xK_t ), withFocused $ windows . W.sink)
-- Increment the number of windows in the master area
, ((modm , xK_comma ), sendMessage (IncMasterN 1))
-- Deincrement the number of windows in the master area
, ((modm , xK_period), sendMessage (IncMasterN (-1)))
-- Quit xmonad
, ((modm .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
-- Restart xmonad
, ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart")
]
++
-- mod-[1..9], Switch to workspace N
-- mod-shift-[1..9], Move client to workspace N
[((m .|. modm, k), windows $ f i)
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
++
myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList $
-- mod-button1, Set the window to floating mode and move by dragging
[ ((modm, button1), (\w -> focus w >> mouseMoveWindow w
>> windows W.shiftMaster))
-- mod-button2, Raise the window to the top of the stack
, ((modm, button2), (\w -> focus w >> windows W.shiftMaster))
-- mod-button3, Set the window to floating mode and resize by dragging
, ((modm, button3), (\w -> focus w >> mouseResizeWindow w
>> windows W.shiftMaster))
-- you may also bind events to the mouse scroll wheel (button4 and button5)
]
myLayout = 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
myManageHook = composeAll
[
className =? "Gimp" --> doFloat
, resource =? "desktop_window" --> doIgnore
]
myEventHook = mempty
myLogHook = return ()
myStartupHook = return ()
main = xmonad defaults
defaults = def {
-- simple stuff
terminal = myTerminal,
focusFollowsMouse = myFocusFollowsMouse,
clickJustFocuses = myClickJustFocuses,
borderWidth = myBorderWidth,
modMask = myModMask,
workspaces = myWorkspaces,
normalBorderColor = myNormalBorderColor,
focusedBorderColor = myFocusedBorderColor,
-- key bindings
keys = myKeys,
mouseBindings = myMouseBindings,
-- hooks, layouts
layoutHook = myLayout,
manageHook = myManageHook,
handleEventHook = myEventHook,
logHook = myLogHook,
startupHook = myStartupHook
}
Now, after restarting Xmonad, we should have a working Xmonad window manager with modifier set to Windows-key. When Win+Shift+Enter are pressed, terminal should pop up, it might look quite ugly, but we will fix that later.
This setup uses rofi it should be available in Debian distribution.
We’ll continue our minimal desktop journey in a next post.
Ok, that’s it. Hopefully you got something out of this and I’ll see you in next post.