Предположим, что делаем все телодвижения в ~/dev. И ставим всё в пределах ~, не загаживая систему. Ещё предположим, что мы лепим связку из xmonad и xmobar, в пользу которого я отказываюсь от связки из моего самописного скрипта и пары dzen2. Остальные программы, которые тут будут тоже настоятельно рекомендуются.
Зависимости
Сначала ставим GHC и хидеры для иксовых библиотек. У меня уже это есть, но вообще было бы
aptitude install ghc6 libx11-dev
.Теперь нам нужен свежий Cabal. Если бы мы использовали ghc 6.8, то там он уж был бы, но его пока не нашлось. Потому делаем весьма тривиальные штуки:
cd ~/dev
wget http://hackage.haskell.org/packages/archive/Cabal/1.2.3.0/Cabal-1.2.3.0.tar.gz
tar xfv Cabal-1.2.3.0.tar.gz
rm -f Cabal-1.2.3.0.tar.gz
cd Cabal-1.2.3.0
runhaskell Setup.lhs configure --user --prefix=$HOME
runhaskell Setup.lhs build
runhaskell Setup.lhs install --user
Пьём чай, курим, разглядываем порнографический комикс, пока оно соберётся. Поставится оно в ~/lib.
Далее нам нужны хаскелльные либы mtl, unix, X11. Первые две уже были довольно свеженьких версий, а последнюю пришлось обновить, но там всё аналогично Cabal'у и нет смысла повторяться. Берётся всё опять же с http://hackage.haskell.org
XMonad, XMonadContrib, xmobar
C xmonad по сути тоже самое. На всякий случай:
wget http://hackage.haskell.org/packages/archive/xmonad/0.5/xmonad-0.5.tar.gz
tar xfv xmonad-0.5.tar.gz
rm -f xmonad-0.5.tar.gz
cd xmonad-0.5
runhaskell Setup.lhs configure --user --prefix=$HOME
runhaskell Setup.lhs build
runhaskell Setup.lhs install --user
Бинарик положится в ~/bin/xmonad и ещё чуть-чуть в ~/lib.
Тут самое интересное то, что теперь конфиг вкомпиливать не надобно в бинарь.
Аналогично делаем с XMonadContrib, ибо голый xmonad весьма гол. Теперь мы имеем в ~/lib ещё и экстеншены.
Также нас интересует программа xmobar, которая берётся всё там же. Она нам будет показывать всякие мелькающие разноцветные циферки. Устанавливается аналогично вышеуказанным.
Склеиваем вместе
Бинари xmonad и xmobar у нас лежат в ~/bin, а потому нам полезно прописать эту директорию в $PATH. У меня, например, это строчка
export PATH="/home/buttons/bin:${PATH}"
в ~/.zhenv. GHC тоже должен быть в $PATH или конфиг всё же придётся самому компилировать, но он скорее всего там уже есть.Создаём директорию ~/.xmonad и там в xmonad.hs будем писать свой конфиг. Можно подсматривать в стандартном из ~/dev/xmonad-5.0/XMonad/Config.hs, можно позаимствовать из вики, можно мой, который будет приведён ниже. Вообще, нам теперь не надо сочинять полноценный конфиг, а достаточно лишь писать то, что отличается от стандартного. Допустим, используется мой.
После правок можно проверять конфиг на синтаксическую и не только правильность при помощи команды
ghci ~/.xmonad/xmonad.hs
. Но вообще xmonad умеет через xmessage сообщать об ошибках компиляции. Перезапускать xmonad толком не надо для применения новых настроек. Обычно достаточно сказать mod-q (если не перенастроено) и новый конфиг будет скомпилирован, слинкован с xmonad и WM перезапустится, не теряя наши окошки.Ну вот и мой конфиг:
import XMonad
import System.Exit
import qualified XMonad.StackSet as W
import qualified Data.Map as M
-- Расширения
import XMonad.Actions.CycleWS
import qualified XMonad.Actions.FlexibleResize as Flex
import XMonad.Actions.SinkAll
import XMonad.Hooks.DynamicLog
import XMonad.Layout.Circle
import XMonad.Layout.NoBorders
import XMonad.Layout.Tabbed
import XMonad.Prompt
import XMonad.Prompt.Man
import XMonad.Prompt.Shell
import XMonad.Prompt.Ssh
-- Мой любимый эмулятор терминала
myTerminal = "urxvtc"
-- Мой шрифт для разных надписей
myFont = "-xos4-terminus-medium-r-normal--12-120-72-72-c-60-*-*"
-- Мои любимые цвета
myNormalBorderColor = "#333"
myFocusedBorderColor = "#fedb73"
-- Устанавливаем функциональной кнопкой "windows key".
myModMask = mod4Mask
-- Отступ от краёв экрана для показывания всяческих сайдбаров
-- У меня это 14 пикселов сверху
myDefaultGaps = [(14,0,0,0)]
-- Мои воркспейсы
myWorkspaces = ["1:im", "2:web", "3:music", "4:emacs"]
++ map show [5 .. 9]
-- Кейбиндинги
myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
-- Запуск терминала
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
-- Блокировка экрана
, ((modMask, xK_Pause ), spawn "slock")
-- Скриншот
, ((modMask, xK_Print ), spawn "import -window root ~/tmp/screenshot-`date +%s`.png")
-- Громкость +/-
, ((modMask .|. shiftMask, xK_Up ), spawn "amixer -q set Master 1+")
, ((modMask .|. shiftMask, xK_Down ), spawn "amixer -q set Master 1-")
-- Убийства окна в фокусе
, ((modMask .|. shiftMask, xK_c ), kill)
-- Схемы расположения окон
-- Переключиться на следующую доступную
, ((modMask, xK_space ), sendMessage NextLayout)
-- Сменить на стандартную
, ((modMask .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
-- Изменить размер видимых окон до корректного
, ((modMask, xK_n ), refresh)
-- Перемещение фокуса
-- На следующее окно
, ((modMask, xK_Tab ), windows W.focusDown)
, ((modMask, xK_j ), windows W.focusDown)
-- На предыдущее окно
, ((modMask, xK_k ), windows W.focusUp )
-- Переместить фокус на мастер-окно
, ((modMask, xK_m ), windows W.focusMaster )
-- Изменение расположения окон
-- Поменять местами окно в фокусе и мастер-окно
, ((modMask, xK_Return), windows W.swapMaster)
-- Поменять местами окно в фокусе со следующим
, ((modMask .|. shiftMask, xK_j ), windows W.swapDown )
-- Поменять местами окно в фокусе с предыдущим
, ((modMask .|. shiftMask, xK_k ), windows W.swapUp )
-- Уменьшить master area
, ((modMask, xK_h ), sendMessage Shrink)
-- Увеличить master area
, ((modMask, xK_l ), sendMessage Expand)
-- Вернуть окно в tiled-режим
, ((modMask, xK_t ), withFocused $ windows . W.sink)
-- Увеличить кол-во окон в master area
, ((modMask , xK_comma ), sendMessage (IncMasterN 1))
-- Уменьшить кол-во окон в master area
, ((modMask , xK_period), sendMessage (IncMasterN (-1)))
-- Переключить показ всяческих сайдбаров
, ((modMask , xK_b ), modifyGap (\i n -> let x = (XMonad.defaultGaps conf ++ repeat (0,0,0,0)) !! i in if n == x then (0,0,0,0) else x))
-- Выйти из WM
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
-- Перезагрузить WM
, ((modMask , xK_q ), broadcastMessage ReleaseResources >> restart (Just "xmonad") True)
-- Кейбиндинги для расширений
-- Вернуть все окна на воркспейсе в tiled-режим
, ((modMask .|. shiftMask, xK_t ), sinkAll)
-- Переключение/перенос окна на следующий/предыдущий воркспейс
, ((modMask, xK_Right), nextWS)
, ((modMask, xK_Left), prevWS)
, ((modMask .|. shiftMask, xK_Right), shiftToNext >> nextWS)
, ((modMask .|. shiftMask, xK_Left), shiftToPrev >> prevWS)
-- Просмотр man-ов
, ((modMask, xK_F1), manPrompt myXPConfig)
-- Запуск какого-нибудь приложения
, ((modMask, xK_p), shellPrompt myXPConfig)
-- Запуск какого-нибудь приложения в эмуляторе терминала
, ((modMask .|. shiftMask, xK_p), prompt ("urxvtc" ++ " -e") myXPConfig)
-- Подключение к какому-нибудь хосту по SSH
, ((modMask, xK_s), sshPrompt myXPConfig)
]
++
-- mod-[1..9] -- переключение на воркспейс #N
-- mod-shift-[1..9] -- перемещение окна на воркспейс #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)]]
-- Биндинги для мышки
myMouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $
-- Переключение окна в floating-режим и перемещение курсором
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w))
-- Перемещение окна на верх "стопки"
, ((modMask, button2), (\w -> focus w >> windows W.swapMaster))
-- Переключение окна в floating-режим и улучшенное изменение размера
, ((modMask, button3), (\w -> focus w >> Flex.mouseResizeWindow w))
]
-- Схемы расположения окон
-- Обычный tiled, ion3-like tabbed, полноэкранный без окантовки, круговой
myLayout = tiled ||| tabbed shrinkText myTabConfig ||| noBorders Full ||| Circle
where
-- В обычном tiled экран разделяется на две панели
tiled = Tall nmaster delta ratio
-- В основном панели по умолчанию одно окно
nmaster = 1
-- Стандартная пропорция разделения экрана на панели
ratio = 1/2
-- Шаг изменения пропорции
delta = 3/100
-- Правила для отдельных окон
-- Используем xprop | grep WM_CLASS для познания идентификатора окна
myManageHook = composeAll
[ className =? "MPlayer" --> doFloat
, className =? "Gimp" --> doFloat
, className =? "psi" --> doF (W.shift "1:im")
, className =? "Firefox-bin" --> doF (W.shift "2:web")
, className =? "Opera" --> doF (W.shift "2:web")
, className =? "Amarokapp" --> doF (W.shift "3:music")
, className =? "Emacs" --> doF (W.shift "4:emacs")]
-- Настройки логирования
myPP = defaultPP { ppCurrent = xmobarColor "yellow" "" . wrap "[" "]"
, ppTitle = xmobarColor "green" "" . shorten 40
, ppVisible = wrap "(" ")"
}
-- Настройки для схемы tabbed
myTabConfig = defaultTConf { activeColor = myFocusedBorderColor
, inactiveColor = myNormalBorderColor
, activeBorderColor = myNormalBorderColor
, inactiveBorderColor = myFocusedBorderColor
, activeTextColor = myNormalBorderColor
, inactiveTextColor = myFocusedBorderColor
, fontName = myFont
, tabSize = 14
}
-- Настройки для промптов
myXPConfig = XPC { font = myFont
, bgColor = myNormalBorderColor
, fgColor = myFocusedBorderColor
, fgHLight = "#000000"
, bgHLight = "#BBBBBB"
, borderColor = myFocusedBorderColor
, promptBorderWidth = 1
, position = Bottom
, height = 14
, historySize = 256
}
------------------------------------------------------------------------
-- Запускаем xmonad с настройками, что мы тут прописывали
main = xmonad defaults
defaults = defaultConfig {
-- простые штуки
terminal = myTerminal,
normalBorderColor = myNormalBorderColor,
focusedBorderColor = myFocusedBorderColor,
modMask = myModMask,
defaultGaps = myDefaultGaps,
workspaces = myWorkspaces,
-- кейбиндинги
keys = myKeys,
mouseBindings = myMouseBindings,
-- хуки, схемы
layoutHook = myLayout,
manageHook = myManageHook,
logHook = dynamicLogWithPP myPP
}
Думаю, комментарии всё проясняют.
Теперь мы настроим xmobar. Для этого в ~/.xmobarrc пишем что-то вроде этого:
Config { font = "-xos4-terminus-medium-r-normal--12-120-72-72-c-60-*-*"
, bgColor = "black"
, fgColor = "grey"
, position = Static { xpos = 0
, ypos = 0
, width = 1280
, height = 14
}
, commands = [ Run Network "eth0" ["-L","10","-H","100","--normal","green","--high","red"] 10
, Run Cpu ["-L","3","-H","50","--normal","green","--high","red"] 10
, Run Memory ["-t","Mem: <usedratio>%"] 10
, Run Swap [] 10
, Run Date "%a %b %_d %Y %H:%M:%S" "date" 10
, Run PipeReader "/home/buttons/.xmonad/status" "xlog"
]
, sepChar = "%"
, alignSep = "}{"
, template = "%xlog% }{ | %cpu% | %memory% * %swap% | %eth0% | <fc=#ee9a00>%date%</fc>"
}
Теперь скрипт, который связывает две программы. Основываемся на скрипте из XMonadContribs. Это будет ~/bin/run-xmonad.sh:
#!/bin/sh
# xmonad use with a pipe talking to an external program
PIPE=$HOME/.xmonad/status
rm -f $PIPE
PATH=${PATH}:/sbin mkfifo -m 600 $PIPE
[ -p $PIPE ] || exit
# status bar
xmobar &
# go for it
xmonad > $PIPE &
# wait for xmonad
wait $!
killall xmobar
Остаётся только дописать запуск этого дела в .xsession или .xinitrc (по вкусу). Например, у меня такой ~/.xsession:
xmodmap ~/.Xmodmap &
urxvtd -q -o -f &
unclutter -idle 1 &
/home/buttons/bin/run-xmonad.sh
Всё. Теперь всё должно работать. Можно смело выходить из своего уютного Гнома или что там.
Что надо сделать в будущем:
Заменить вывод в статусбар и другие места чем-то, что умеет юникоды. Заменить ввод Prompt.hs чем-то, что умеет юникоды.
Ещё надо сочинить нормальную расцветочку, т.к. из-за лени у меня полный вырви глаз получился.
← Ctrl ← Alt
Ctrl → Alt →
← Ctrl ← Alt
Ctrl → Alt →