Documentation. Use LHS.

Ilya Portnov [2011-05-28 17:12:12]
Documentation. Use LHS.
Filename
lib/AppGroups.lhs
lib/Layouts.hs
lib/Layouts.lhs
lib/MPD.hs
lib/MPD.lhs
lib/MyManageHooks.hs
lib/MyManageHooks.lhs
lib/Themes.lhs
lib/Volume.hs
lib/Volume.lhs
xmonad.hs
diff --git a/lib/AppGroups.lhs b/lib/AppGroups.lhs
index 6fec1bc..16a6c15 100644
--- a/lib/AppGroups.lhs
+++ b/lib/AppGroups.lhs
@@ -4,13 +4,14 @@ AppGroups — группы приложений
 Описание
 --------

-Группа приложений задаётся набором условий на окна. Этот модуль позволяет описать в одном месте
-и ManageHook-и, и сочетания клавиш для групп приложений. Кроме того, с группой может быть связано
-действие. Это действие запускается вместо переключения на группу, в том случае, если окон в группе
-сейчас нет.
+Группа приложений задаётся набором условий на окна. Этот модуль позволяет
+описать в одном месте и ManageHook-и, и сочетания клавиш для групп приложений.
+Кроме того, с группой может быть связано действие. Это действие запускается
+вместо переключения на группу, в том случае, если окон в группе сейчас нет.

 Заголовки модуля
 ----------------
+
 Включаем нужные расширения языка. Здесь используются Existentials для
 организации гетерогенных списков.

diff --git a/lib/Layouts.hs b/lib/Layouts.hs
deleted file mode 100644
index 2e1ac79..0000000
--- a/lib/Layouts.hs
+++ /dev/null
@@ -1,111 +0,0 @@
-{-# LANGUAGE ExistentialQuantification, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, TypeSynonymInstances, DeriveDataTypeable, NoMonomorphismRestriction #-}
-module Layouts where
-
-import Data.Ratio ((%))
-
-import XMonad hiding ((|||))
-import XMonad.Hooks.ManageDocks hiding (L,R)
-import XMonad.Util.WindowPropertiesRE
-
--- Import contrib layouts
-import XMonad.Layout.AutoMaster
-import XMonad.Layout.Column
-import XMonad.Layout.Decoration
-import XMonad.Layout.PerWorkspace
-import XMonad.Layout.LayoutCombinators
-import XMonad.Layout.Named
-import XMonad.Layout.GridVariants
-import XMonad.Layout.IM
-import XMonad.Layout.Reflect
-import XMonad.Layout.MagicFocus
-import XMonad.Layout.CenteredMaster
-import XMonad.Layout.OneBig
-import XMonad.Layout.NoBorders
-import XMonad.Layout.ComboP
-import XMonad.Layout.TwoPane
-import XMonad.Layout.DecorationAddons
-import XMonad.Layout.ButtonDecoration
-import XMonad.Layout.WindowSwitcherDecoration
-import XMonad.Layout.DraggingVisualizer
-import XMonad.Layout.Tabbed
-import XMonad.Layout.TrackFloating
-import XMonad.Layout.Maximize
-import XMonad.Layout.Minimize
-import XMonad.Layout.Simplest
-import XMonad.Layout.Groups
-import XMonad.Layout.LayoutBuilderP
-import XMonad.Layout.AutoComboP
-import XMonad.Layout.TwoPane
-
-import qualified XMonad.Layout.WindowNavigation as Nav
-
-import Themes
-
-lugChats = RE (Title "lug-mgn" `Or` Title "math")
-xmonadChats = RE (Title "xmonad")
-programmingChats = RE (Title "programming" `Or` Title "haskell" `Or` Title "python")
-
-fullscreenRect = Just $ relBox 0 0 1 1
-
-----------------------------------------------------
-tWithIM = withIM (1%6) (Role "buddy_list")
-
-isGfxPanel = Role "gimp-toolbox" `Or` Role "Brush selector" `Or` Role "toolbox_window" `Or` Role "Layers"
-
-tabs = tabbed shrinkText deco
-
--- imlayout = addTabs shrinkText deco $ subLayout [] Simplest (autoMaster 2 (1/100) $ Grid 2)
-imlayout = (layoutP programmingChats (relBox 0 0 0.55 0.5) fullscreenRect  tabs)
-           $ (layoutP xmonadChats    (relBox 0.55 0 1 0.5) Nothing         tabs)
-           $ (layoutP lugChats       (relBox 0.45 0.5 1 1) (Just $ relBox 0 0.5 1 1) tabs)
-           $ (layoutAll (relBox 0 0.5 0.45 1) tabs)
-tabgrid  = group tabs (Mirror $ Tall 1 (1/100) (2/3))
-tabfull  = addTabs shrinkText deco Simplest
-
-grid = named "grid" $ Grid (2)
-vgrid = Grid (1/2)
-tiled = Tall 1 (1/100) (1/2)
-dwmtile = named "dwm" tiled
--- full = named "Full" $ tabbedBottom shrinkText deco
-full = Full
-mix = named "mix" $ Tall 1 (1/100) (2/3)
-mirrored = named "mirror" $ Mirror tiled
--- forgimp = named "gimp" $ reflectHoriz (Tall 1 (1/100) (1/4))
-zgrid =  magicFocus (centerMaster grid)
-autogrid = named "autogrid" (autoMaster 1 (1/100) grid)
-autotall = Mirror (autoMaster 1 (1/100) vgrid)
-autogrid2 = autoMaster 2 (1/100) grid
-books = named "books" (Tall 1 (1/100) (2/3))
--- rowtile = Mirror hortile
--- mgrid = centerMaster grid
--- forim = named "im" (tWithIM (minimax grid ||| minimax autogrid2 ||| zgrid ||| Full))
-forim = named "im" $ tWithIM (imlayout ||| tabs)
-column = Column 1.8
-forgimp = named "gimp" $ withButtons $ combineTwoP (TwoPane 0.03 0.75) column (reflectVert $ Column 0.4) $ Not isGfxPanel
-onebig = named "onebig" $ (OneBig (3/4) (3/4))
--- coding = named "coding" $ reflectHoriz $ mastered (1/100) (1/3) (Column 2)
-
-fortext = autoCombineTwoP 0.1 0.1 0.03 (Mirror $ TwoPane 0.03 0.5) tabs grid (ClassName "Gvim")
-
-minimax = maximize . minimize
-
-withButtons = buttonDeco shrinkText decoB
-
-draggable layout = windowSwitcherDecorationWithButtons shrinkText decoB (draggingVisualizer $ layout)
--- draggableButtons = id
-
--- draggable layout = windowSwitcherDecoration shrinkText decoB (draggingVisualizer $ layout)
--- draggable = id
-
-myLayout = trackFloating $
-           smartBorders $
-           Nav.configurableNavigation (Nav.navigateBrightness 0.0) $
-           avoidStruts $
-           -- Tab.modify shrinkText deco $
-           onWorkspace "inet" (minimax full) $
-           onWorkspace "text" (minimax fortext ||| minimax autogrid2 ||| minimax dwmtile ||| minimax mirrored ||| books ||| autogrid ||| onebig ) $
-           onWorkspace "files" (minimax full ||| minimax dwmtile ||| autogrid) $
-           onWorkspace "im" forim $
-           onWorkspace "term" (minimax full ||| minimax mirrored ||| autogrid) $
-           onWorkspace "gimp" (forgimp ||| full ||| grid) $
-           onWorkspace "trash" (full ||| autogrid ||| grid) (minimax mirrored ||| full)
diff --git a/lib/Layouts.lhs b/lib/Layouts.lhs
new file mode 100644
index 0000000..93667e1
--- /dev/null
+++ b/lib/Layouts.lhs
@@ -0,0 +1,134 @@
+Layouts
+=======
+
+Заголовок и импорты
+-------------------
+
+> {-# LANGUAGE ExistentialQuantification, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, TypeSynonymInstances, DeriveDataTypeable, NoMonomorphismRestriction #-}
+> module Layouts where
+
+> import Data.Ratio ((%))
+
+> import XMonad hiding ((|||))
+> import XMonad.Hooks.ManageDocks hiding (L,R)
+
+Этот импорт из xmonad-extras.
+
+> import XMonad.Util.WindowPropertiesRE
+
+Импорты из xmonad-contrib.
+
+> import XMonad.Layout.AutoMaster
+> import XMonad.Layout.Column
+> import XMonad.Layout.Decoration
+> import XMonad.Layout.PerWorkspace
+> import XMonad.Layout.LayoutCombinators
+> import XMonad.Layout.Named
+> import XMonad.Layout.GridVariants
+> import XMonad.Layout.IM
+> import XMonad.Layout.Reflect
+> import XMonad.Layout.MagicFocus
+> import XMonad.Layout.CenteredMaster
+> import XMonad.Layout.OneBig
+> import XMonad.Layout.NoBorders
+> import XMonad.Layout.ComboP
+> import XMonad.Layout.TwoPane
+> import XMonad.Layout.DecorationAddons
+> import XMonad.Layout.ButtonDecoration
+> import XMonad.Layout.WindowSwitcherDecoration
+> import XMonad.Layout.DraggingVisualizer
+> import XMonad.Layout.Tabbed
+> import XMonad.Layout.TrackFloating
+> import XMonad.Layout.Maximize
+> import XMonad.Layout.Minimize
+> import XMonad.Layout.Simplest
+> import XMonad.Layout.Groups
+> import XMonad.Layout.LayoutBuilderP
+> import XMonad.Layout.AutoComboP
+> import XMonad.Layout.TwoPane
+
+> import qualified XMonad.Layout.WindowNavigation as Nav
+
+Модуль конфига.
+
+> import Themes
+
+Вспомогательные определения
+---------------------------
+
+Группы чатов.
+
+> lugChats = RE (Title "lug-mgn" `Or` Title "math")
+> xmonadChats = RE (Title "xmonad")
+> programmingChats = RE (Title "programming" `Or` Title "haskell" `Or` Title "python")
+
+> fullscreenRect = Just $ relBox 0 0 1 1
+
+Модификатор для всяческих ростеров.
+
+> withRoster = withIM (1%6) (Role "buddy_list")
+
+> isGfxPanel = Role "gimp-toolbox" `Or` Role "Brush selector" `Or` Role "toolbox_window" `Or` Role "Layers"
+
+Определения Layout-ов
+---------------------
+
+Простой layout со вкладками.
+
+> tabs = tabbed shrinkText deco
+
+Layout для окон Pidgin. Раскладывает окна с чатами по областям с вкладками, по тематике.
+
+> imlayout = (layoutP programmingChats (relBox 0 0 0.55 0.5) fullscreenRect  tabs)
+>            $ (layoutP xmonadChats    (relBox 0.55 0 1 0.5) Nothing         tabs)
+>            $ (layoutP lugChats       (relBox 0.45 0.5 1 1) (Just $ relBox 0 0.5 1 1) tabs)
+>            $ (layoutAll (relBox 0 0.5 0.45 1) tabs)
+
+> tabgrid  = group tabs (Mirror $ Tall 1 (1/100) (2/3))
+> tabfull  = addTabs shrinkText deco Simplest
+
+> grid = named "grid" $ Grid (2)
+> vgrid = Grid (1/2)
+> tiled = Tall 1 (1/100) (1/2)
+> dwmtile = named "dwm" tiled
+
+> full = Full
+> mix = named "mix" $ Tall 1 (1/100) (2/3)
+> mirrored = named "mirror" $ Mirror tiled
+> -- forgimp = named "gimp" $ reflectHoriz (Tall 1 (1/100) (1/4))
+> zgrid =  magicFocus (centerMaster grid)
+> autogrid = named "autogrid" (autoMaster 1 (1/100) grid)
+> autotall = Mirror (autoMaster 1 (1/100) vgrid)
+> autogrid2 = autoMaster 2 (1/100) grid
+> books = named "books" (Tall 1 (1/100) (2/3))
+
+> forim = named "im" $ withRoster (imlayout ||| tabs)
+> column = Column 1.8
+> forgimp = named "gimp" $ withButtons $ combineTwoP (TwoPane 0.03 0.75) column (reflectVert $ Column 0.4) $ Not isGfxPanel
+> onebig = named "onebig" $ (OneBig (3/4) (3/4))
+
+
+> fortext = autoCombineTwoP 0.1 0.1 0.03 (Mirror $ TwoPane 0.03 0.5) tabs grid (ClassName "Gvim")
+
+> minimax = maximize . minimize
+
+> withButtons = buttonDeco shrinkText decoB
+
+> draggable layout = windowSwitcherDecorationWithButtons shrinkText decoB (draggingVisualizer $ layout)
+
+Главное определение
+-------------------
+
+> myLayout = trackFloating $
+>            smartBorders $
+>            Nav.configurableNavigation (Nav.navigateBrightness 0.0) $
+>            avoidStruts $
+>            -- Tab.modify shrinkText deco $
+>            onWorkspace "inet" (minimax full) $
+>            onWorkspace "text" (minimax fortext ||| minimax autogrid2 ||| minimax dwmtile ||| minimax mirrored ||| books ||| autogrid ||| onebig ) $
+>            onWorkspace "files" (minimax full ||| minimax dwmtile ||| autogrid) $
+>            onWorkspace "im" forim $
+>            onWorkspace "term" (minimax full ||| minimax mirrored ||| autogrid) $
+>            onWorkspace "gimp" (forgimp ||| full ||| grid) $
+>            onWorkspace "trash" (full ||| autogrid ||| grid) (minimax mirrored ||| full)
+
diff --git a/lib/MPD.hs b/lib/MPD.hs
deleted file mode 100644
index 5af2e80..0000000
--- a/lib/MPD.hs
+++ /dev/null
@@ -1,35 +0,0 @@
-module MPD
-  (togglePlaying,
-   stopPlaying,
-   nextTrack,
-   previousTrack
-  ) where
-
-import XMonad
-
-import Network.MPD
-
-import Themes (mpdHost, mpdPort)
-
-mpd :: MPD a -> X ()
-mpd action = do
-  io $ withMPDEx mpdHost mpdPort "" action
-  return ()
-
-togglePlaying :: X ()
-togglePlaying = mpd toggle
-  where
-    toggle = status >>= \st ->
-      case stState st of
-        Playing -> pause True
-        _       -> play Nothing
-
-stopPlaying :: X ()
-stopPlaying = mpd stop
-
-nextTrack :: X ()
-nextTrack = mpd next
-
-previousTrack :: X ()
-previousTrack = mpd previous
-
diff --git a/lib/MPD.lhs b/lib/MPD.lhs
new file mode 100644
index 0000000..94f052b
--- /dev/null
+++ b/lib/MPD.lhs
@@ -0,0 +1,52 @@
+Управление MPD
+==============
+
+Заголовок и импорты
+-------------------
+
+> module MPD
+>   (togglePlaying,
+>    stopPlaying,
+>    nextTrack,
+>    previousTrack
+>   ) where
+
+> import XMonad
+
+Импорт из пакета libmpd.
+
+> import Network.MPD
+
+Модуль конфига.
+
+> import Themes (mpdHost, mpdPort)
+
+Определения
+-----------
+
+Вспомогательная «обёртка».
+
+> mpd :: MPD a -> X ()
+> mpd action = do
+>   io $ withMPDEx mpdHost mpdPort "" action
+>   return ()
+
+Играть/пауза.
+
+> togglePlaying :: X ()
+> togglePlaying = mpd toggle
+>   where
+>     toggle = status >>= \st ->
+>       case stState st of
+>         Playing -> pause True
+>         _       -> play Nothing
+
+> stopPlaying :: X ()
+> stopPlaying = mpd stop
+
+> nextTrack :: X ()
+> nextTrack = mpd next
+
+> previousTrack :: X ()
+> previousTrack = mpd previous
+
diff --git a/lib/MyManageHooks.hs b/lib/MyManageHooks.hs
deleted file mode 100644
index e64b784..0000000
--- a/lib/MyManageHooks.hs
+++ /dev/null
@@ -1,94 +0,0 @@
-{-# LANGUAGE NoMonomorphismRestriction #-}
-module MyManageHooks
-  (myManageHook)
-  where
-
--- To be able to query X11 atoms and such on
-import Foreign.C.Types (CLong)
-import Graphics.X11.Xlib.Extras
-
-import Data.Char     (toLower)
-
-import XMonad
-import qualified XMonad.StackSet as W
-import XMonad.Hooks.ManageHelpers hiding (C)
-import XMonad.Actions.DynamicWorkspaces
-
-import AppGroups
-import GroupsSetup     (myApps)
-import CommonFunctions ((~?), isFloat)
-
-myManageHook = basehooks <+> manageMenus <+> manageDialogs <+> floatPlasma
-
-------------------------------------------------------------------------
--- Window rules:
-
--- Float all menus
-manageMenus = checkMenu --> doFloat
--- Float all dialogs
-manageDialogs = checkDialog --> doFloat
-
--- Check if window has named atom with given value
-checkAtom name value = ask >>= \w -> liftX $ do
-                a <- getAtom name
-                val <- getAtom value
-                mbr <- getProp w a
-                case mbr of
-                  Just [r] -> return $ elem (fromIntegral r) [val]
-                  _ -> return False
-
--- Check if window is a menu (for Gimp tear-off menus, for example)
-checkMenu = checkAtom "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_MENU"
--- Check if window is a dialog
-checkDialog = checkAtom "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_DIALOG"
--- checkDesktop = checkAtom "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_DESKTOP"
--- checkDockAtom = checkAtom "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_DOCK"
--- checkOverride = checkAtom "_NET_WM_WINDOW_TYPE" "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"
--- checkFullscreen = checkAtom "_NET_WM_STATE" "_NET_WM_STATE_FULLSCREEN"
-
--- | Helper to read a property
-getProp :: Window -> Atom -> X (Maybe [CLong])
-getProp w a = withDisplay $ \dpy -> io $ getWindowProperty32 dpy a w
-
--- mNot = liftM not
-
-windowWorkspace :: Window -> X WorkspaceId
-windowWorkspace win = do
-    cls <- withDisplay $ \d -> fmap resName $ io $ getClassHint d win
-    let cls' = map (anySeparatorToSpace . toLower) cls
-        wksp = head $ words cls'
-    return wksp
-  where
-    anySeparatorToSpace c | c `elem` "-._#" = ' '
-                          | otherwise       = c
-
-moveToOwnWorkspace :: Apps -> ManageHook
-moveToOwnWorkspace apps = do
-  window <- ask
-  matching <- liftX $ runQuery (oneOf $ map query apps) window
-  floating <- liftX $ isFloat window
-  if matching || floating
-    then doF id
-    else do
-      wksp <- liftX (windowWorkspace window)
-      if (wksp `notElem` notCreateWorkspace)
-        then do
-          liftX $ addWorkspace wksp
-          doF (W.shift wksp)
-        else doF id
-
-basehooks = composeOne (apps2hooks myApps) <+> moveToOwnWorkspace myApps <+> transience'
-
-notCreateWorkspace = ["gmrun", "plasma", "gcolor2", "ksnapshot"]
-
-floatPlasma = composeAll
-    [ className =? "Qt-subapplication" --> doFloat,
-      title =? "Qt-subapplication" --> doFloat,
-      title =? "Recently opened documents" --> doCenterFloat,
-      className ~? "[pP]lasma-desktop" --> doFloat,
-      className =? "XCalendar" --> doFloat,
-      className =? "Qwerty.py" --> doCenterFloat,
-      title =? "Копирование" --> doFloat,
-      title =? "Перемещение" --> doFloat,
-      className =? "dashboard" --> doFullscreen ]
-
diff --git a/lib/MyManageHooks.lhs b/lib/MyManageHooks.lhs
new file mode 100644
index 0000000..2a3a5c1
--- /dev/null
+++ b/lib/MyManageHooks.lhs
@@ -0,0 +1,89 @@
+ManageHooks
+===========
+
+Заголовки и импорты
+-------------------
+
+> {-# LANGUAGE NoMonomorphismRestriction #-}
+> module MyManageHooks
+>   (myManageHook)
+>   where
+
+> import Data.Char
+
+> import XMonad
+> import qualified XMonad.StackSet as W
+> import XMonad.Hooks.ManageHelpers hiding (C)
+> import XMonad.Actions.DynamicWorkspaces
+
+> import CommonFunctions
+> import AppGroups
+> import GroupsSetup     (myApps)
+
+Правила для окон
+----------------
+
+Главное определение.
+
+> myManageHook = basehooks <+> manageMenus <+> manageDialogs <+> floatPlasma
+
+Сделать плавающими отрывающиеся меню
+
+> manageMenus = checkMenu --> doFloat
+>   where
+>     checkMenu = isInProperty "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_MENU"
+
+Сделать плавающими диалоги
+
+> manageDialogs = isDialog --> doFloat
+
+Основная часть +ManageHook+-ов
+
+> basehooks = composeOne (apps2hooks myApps) <+> moveToOwnWorkspace myApps <+> transience'
+
+Сделать плавающими некоторые окна.
+
+> floatPlasma = composeAll
+>     [ className =? "Qt-subapplication" --> doFloat,
+>       title =? "Qt-subapplication" --> doFloat,
+>       title =? "Recently opened documents" --> doCenterFloat,
+>       className ~? "[pP]lasma-desktop" --> doFloat,
+>       className =? "XCalendar" --> doFloat,
+>       className =? "Qwerty.py" --> doCenterFloat,
+>       title =? "Копирование" --> doFloat,
+>       title =? "Перемещение" --> doFloat ]
+
+«Придумать» название рабочего места, на которое поместить окно. За основу
+берётся свойство WM_CLASS.
+
+> windowWorkspace :: Window -> X WorkspaceId
+> windowWorkspace win = do
+>     cls <- withDisplay $ \d -> fmap resName $ io $ getClassHint d win
+>     let cls' = map (anySeparatorToSpace . toLower) cls
+>         wksp = head $ words cls'
+>     return wksp
+>   where
+>     anySeparatorToSpace c | c `elem` "-._#" = ' '
+>                           | otherwise       = c
+
+Для таких окон специальные рабочие места создаваться не будут.
+
+> notCreateWorkspace = ["gmrun", "plasma", "gcolor2", "ksnapshot"]
+
+Перемещать каждое приложение, не входящее ни в одну из групп, на своё рабочее место.
+
+> moveToOwnWorkspace :: Apps -> ManageHook
+> moveToOwnWorkspace apps = do
+>   window <- ask
+>   matching <- liftX $ runQuery (oneOf $ map query apps) window
+>   floating <- liftX $ isFloat window
+>   if matching || floating
+>     then doF id
+>     else do
+>       wksp <- liftX (windowWorkspace window)
+>       if (wksp `notElem` notCreateWorkspace)
+>         then do
+>           liftX $ addWorkspace wksp
+>           doF (W.shift wksp)
+>         else doF id
+>
diff --git a/lib/Themes.lhs b/lib/Themes.lhs
index 4b32d70..7493916 100644
--- a/lib/Themes.lhs
+++ b/lib/Themes.lhs
@@ -21,12 +21,14 @@ Themes — настройки внешнего вида
 Определения
 -----------

+Параметры соединения с сервером MPD: имя хоста и порт.
+
 > mpdHost = "portnov.local"
 > mpdPort = 6600

 Шрифт.

-> myFont = "xft:Ubuntu-10"
+> myFont = "xft:Ubuntu-11"

 Тема для +X.Prompt+.

diff --git a/lib/Volume.hs b/lib/Volume.hs
deleted file mode 100644
index 554190e..0000000
--- a/lib/Volume.hs
+++ /dev/null
@@ -1,29 +0,0 @@
-
-module Volume
-  (changeVolumeBy,
-   toggleMute) where
-
-import Control.Monad (when)
-
-import XMonad
-
-import Sound.ALSA.Mixer
-
-changeVolumeBy :: Integer -> X ()
-changeVolumeBy i = io $ do
-   result <- getControlByName "default" "Master"
-   whenJust result $ \control ->
-     whenJust (playback $ volume control) $ \playbackVolume -> do
-       (min, max) <- getRange playbackVolume
-       mbVol <- getChannel FrontLeft $ value $ playbackVolume
-       whenJust mbVol $ \vol ->
-         when ((i > 0 && vol < max) || (i < 0 && vol > min))
-             $ setChannel FrontLeft (value $ playbackVolume) $ vol + i
-
-toggleMute :: X ()
-toggleMute = io $ do
-   result <- getControlByName "default" "Master"
-   whenJust result $ \control ->
-     whenJust (playback $ switch control) $ \playbackSwitch -> do
-       mbsw <- getChannel FrontLeft playbackSwitch
-       whenJust mbsw $ \sw -> setChannel FrontLeft playbackSwitch $ not sw
diff --git a/lib/Volume.lhs b/lib/Volume.lhs
new file mode 100644
index 0000000..a819833
--- /dev/null
+++ b/lib/Volume.lhs
@@ -0,0 +1,44 @@
+Управление громкостью через ALSA
+================================
+
+Заголовок и импорты
+-------------------
+
+> module Volume
+>   (changeVolumeBy,
+>    toggleMute) where
+
+> import Control.Monad (when)
+
+> import XMonad
+
+Импорт из пакета alsa-mixer.
+
+> import Sound.ALSA.Mixer
+
+Определения
+-----------
+
+Изменить громкость (в канале Master) на i пунктов.
+
+> changeVolumeBy :: Integer -> X ()
+> changeVolumeBy i = io $ do
+>    result <- getControlByName "default" "Master"
+>    whenJust result $ \control ->
+>      whenJust (playback $ volume control) $ \playbackVolume -> do
+>        (min, max) <- getRange playbackVolume
+>        mbVol <- getChannel FrontLeft $ value $ playbackVolume
+>        whenJust mbVol $ \vol ->
+>          when ((i > 0 && vol < max) || (i < 0 && vol > min))
+>              $ setChannel FrontLeft (value $ playbackVolume) $ vol + i
+
+Включить/выключить звук (в канале Master).
+
+> toggleMute :: X ()
+> toggleMute = io $ do
+>    result <- getControlByName "default" "Master"
+>    whenJust result $ \control ->
+>      whenJust (playback $ switch control) $ \playbackSwitch -> do
+>        mbsw <- getChannel FrontLeft playbackSwitch
+>        whenJust mbsw $ \sw -> setChannel FrontLeft playbackSwitch $ not sw
+
diff --git a/xmonad.hs b/xmonad.hs
index 1a450ef..7ef6fc6 100644
--- a/xmonad.hs
+++ b/xmonad.hs
@@ -33,7 +33,7 @@ baseLogHook = logHook baseConfig
 main =  do
   replace
   xmonad $ ewmh $ baseConfig {
-        terminal           = "gnome-terminal",
+        terminal           = myTerminal,
         focusFollowsMouse  = False,
         borderWidth        = myBorderWidth,
         modMask            = mod4Mask,
ViewGit