XMonad + XMobar = ❤

в 9:18, , рубрики: amixer, archlinux, haskell, mkfifo, nix, Настройка Linux

Многие слышали про тайловые оконные менеджеры, некоторые даже слышали о XMonad. А ребята из Google даже променяли Unity/Gnome на XMonad. Что же это такое, как это настраивать и как с этим жить? Краткий workaround для любителей кастомизировать всё подряд.

XMonad + XMobar = ❤

Дружище, зачем же оно мне?

Для себя я выделил несколько ситуаций, когда он особенно полезен особенно полезен:

  • Рабочий стол ноутбука для разработки. Места нехватает, очень хочется использовать его максимально эффективно;
  • Многопониторные рабочие места. XMonad позволяет во всю использовать преимущества нескольких мониторв с помощью xinerma;
  • Частое переключение контекстов задач. Например, разработчику часто приходится переключаться между IDE, файловым менеджером, консолью и прочими инструментами. Этот процесс можно ускорить;
  • Надоел визуальный шум от стандартных оконных менеджеров.

В общем, меньше слов, больше дела! Если хочешь управляться с рабочим окружением так же ловко, как герой сериала Kopps управляется с преступниками XMonad + XMobar = ❤, то ты не зря зашёл.

Немного о коде и примерах

Я использую archlinux, поэтому все примеры будут в разрезе archlinux. Но на других линукса все действия производятся похожим образом.

Все примеры я старался оформить в виде скриптов, чтобы потом можно было развернуть на новой системе. Буду оставлять ссылки на github.

Шаг 1: Установка XMonad

Данное действие специфичено для archlinux
Первое, что нужно сделать — это поставить yaourt, если он не стоит конечно же. Не буду утомлять, я делаю это скриптом install-01-yaourt.sh. (Когда нашел на просторах интернета, спасибо автору.)

Ставим XMonad:

yaourt -S xmonad xmonad-contrib xmobar dmenu2

Или, если хотим поставить из haskell-core репозитория:

yaourt -S haskell-xmonad haskell-xmonad-contrib xmobar dmenu2

Так же можно поставить через yaourt darcs версию, но этот вирант мы опустим.

Шаг 2: Минимальная настройка XMonad

Конфигурирование XMonad производится с помощью файла ~/.xmonad/xmonad.hs. Как вы уже наверно догадались, конфигурация пишется на Haskell.

Если вы не знаете Haskell, это не беда, польюзуйтесь чужими конфигами. Когда конфиг работает, понять что к чему можно и на понятийном уровне.

Минимальный .xmonad/xmonad.hs:

import XMonad

main = xmonad defaultConfig
        { modMask = mod4Mask -- Use Super instead of Alt
        , terminal = "urxvt"
        }

Курс молодого бойца по управлению XMonad

При использовании тайлового оконного менеджера, окон как таковых — нет. Есть тайлы. Тайлы ведут себя в соответсвии с текущем Layout автоматически располагаясь так или иначе (например, Layout Grid — тупо отображает таблицой или же Full — отобразить одно главное окно).

Layout

Компанует тайлы. По умолчанию переключение между Layout`s производится с помощью сочетания modKey+space.
В нашем случае modKey = клавише Windows(Super).

Тайл

Это и есть те самые, привычные нам окошки.

В Layout может быть настроена поддержка изменения размера тайлов и количества мастер окон. Так же можно «отсоединить» тайл, зажав кнопку modKey и потянув за окно мышкой. А modKey+RightMouseButton будет изменять размер окошка. Перевести тайл из режима doFloat в стандартный можно комбинацией modKey+t.

Мастер тайл — тайл, или группа тайлов, которые будут отображаться на «самом видном месте». На скришоте в шапке, master тайл самый большой, его сразу видно.

Между тайлами можно переключаться сочетаниями modKey+tab или modKey+j/modKey+k — фокус на предыдущий/следующий тайл. Так же тайлы можно менять местами с мастер тайлом или предыдущими: modKey+entеr — поменять местами текущий тайл и мастер тайл. modKey+J/modKey+K — поменять местами со следующим/предыдущим

Workspace

Это по сути, рабочие столы, для каждого workspace Layout свой и сохраняется при переключении на другие рабочие столы по возвращению. Хотя это поведение можно изменить, но это уже другая история.

Переключаться между Wokspace можно с помощью сочетания modKey+#, где # — номер рабочего стола (не на цифровой клавиатуре).

Подведём итог:

modKey+tab — фокус на следующий (с шифтом на следующий);
modKey+j/modKey+k — фокус на следующий/предыдущий;
modKey+J/modKey+K — поменять текущий и следующий/предыдущий;
modKey+entеr — поменять текущий тайл и мастер тайл местами;
modKey+m — фокус на мастер тайл;
modKey+# — рабочий стол №#;
modKey+t — перевести тайл из режима doFloat в стандартный.

Оставлю это тут: xmonad cheatsheet

Шаг 3: Запуск

Есть много способов запустить XMonad, рассмотрим парочку.

1. Чистый запуск

У нас установлены только XMonad, нам не нужен ни Gnome, ни KDE, ни XFCE. Мы сами по себе.
В этом случае для запуска достаточно сконфигурировать запуск xmonad в файле инициализации X системы: .xinitrc:

setxkbmap -layout "us,ru"
setxkbmap -option "grp:caps_toggle,grp_led:scroll"

xsetroot -cursor_name left_ptr &

export PATH=~/bin:$PATH

#exec startxfce4
#exec startkde
#exec gnome-session
exec dbus-launch xmonad

Полезняшка: мой .xinitrc

Теперь при запуске startx запустется xmonad (конечно же, эта команда исполняется, когда ещё нет X сессии).

2. Замена текущего оконного менеджера

XMonad умеет нагло заменять текущий window manager. Для этого достаточно выполнить простую команду:

xmonad --replace

3. Интеграция с __ПодставитьЧтоНужно__

А подставить можно, например, GNOME (дока для archlinux).
Тут, я думаю, каждый найдёт то, что ему нужно, мы же двинемся к самому интересному.

Шаг 4: Нескучные обои

Если вы запустили xmonad с минимальным конфигом, то уже, наверно, заметили, что там чёрный экран. Обоев нет!
Как же так, подумали мы, в gnome есть, в kde есть… И тут сделаем!

Быстро исправляем это недоразумение:

sudo pacman -S feh
feh --bg-center /usr/share/backgrounds/gnome/Bokeh_Tails.jpg # Тут можно подставить путь до любой картинки. Или, о господи, даже http урл!
feh --bg-center http://habrastorage.org/files/c1a/90a/763/c1a90a763acc48a083a325d42f86f8ad.jpg # да да, даже так!

Вам, наверно, понравилось, да? Можно поставить любимую картинку из интернета прямо на рабочий стол, зафиксируем результат:

echo "feh --bg-center http://habrastorage.org/files/c1a/90a/763/c1a90a763acc48a083a325d42f86f8ad.jpg &">>~/.xinitrc

Шаг 5: Рашпиль

Ну вот теперь можно работать, возьмём в руки инструмент и начнём допиливать XMonad.

Тут можно было бы приложить конфиг и сказать — готово, но нет. Мы же сдесь чтобы хоть чуть чуть разобраться.
Поработаем над нашим конфигом, преобразуем его к следующему виду:

defaults = defaultConfig {
          terminal                 = "urxvt"
        , workspaces           = myWorkspaces
        , modMask              = mod4Mask
        , borderWidth          = 2
	}

myWorkspaces :: [String]
myWorkspaces =  ["1:web","2:dev","3:term","4:vm","5:media"] ++ map show [6..9]

main = do
        xmonad defaults

Теперь наши рабочие столы с названиями, но ты, дядя Фёдор, этого всё равно не увидишь, ведь у тебя xmobar не настроен!
Ладно, отложим xmobar, сделаем нечто более существенное:


defaults = defaultConfig {
          terminal                 = "urxvt"
        , workspaces           = myWorkspaces
        , modMask              = mod4Mask
        , layoutHook            = myLayoutHook
        , handleEventHook  = fullscreenEventHook  -- для корректного отображения окон в полно экранном режиме
        , startupHook           = setWMName "LG3D"  -- для совместимости определёных приложений, java например(IntelliJ IDEA)
        , borderWidth           = 2
        , normalBorderColor  = "black"
        , focusedBorderColor  = "orange"   
	}

myWorkspaces :: [String]
myWorkspaces =  ["1:web","2:dev","3:term","4:vm","5:media"] ++ map show [6..9]

myLayoutHook = spacing 6 $ gaps [(U,15)] $ toggleLayouts (noBorders Full) $
    smartBorders $ Mirror tiled ||| mosaic 2 [3,2]  ||| tabbed shrinkText myTabConfig
      where 
        tiled = Tall nmaster delta ratio
        nmaster = 1
        delta   = 3/100
        ratio   = 3/5

main = do
        xmonad defaults

Теперь посмотрим что получилось, для этого:

  • перекомпилим командой xmonad --recompile
  • если нет ошибок перезапустим xmonad --restart

И, вуаля, изменения должны вступить в силу.

Выполним комбинацию modKey+shift+enter. Должнен открыться тайл с консолью. Если не открылся, возможно, проблема в строчке конфига xmonad.hs:

          terminal                 = "urxvt"

Укажите терминал, которые есть в системе, или установите urxvt:


sudo pacman -S rxvt-unicode

Сочетанием modKey+p можно вызвать dmenu (откроется сверху, набирая команду, там будет отобажён список приложений, которые можно запустить). Вызовем gvim, например, modKey+p -> печатаем gvim (если он, конечно, есть в системе) жмакаем enter.

Итак, у нас есть рабочие столы, тайлы, лайауты… Так давайте же автоматически направлять приложения на разные рабочие столы? Давайте.

Идём в наш конфиг xmonad.hs и добавляем следующее:

...
defaults = defaultConfig {
          terminal                 = "urxvt"
        , workspaces           = myWorkspaces
        , modMask              = mod4Mask
        , layoutHook            = myLayoutHook
        , handleEventHook  = fullscreenEventHook  -- для корректного отображения окон в полно экранном режиме
        , startupHook           = setWMName "LG3D"  -- для совместимости определёных приложений, java например(IntelliJ IDEA)
        , borderWidth           = 2
        , manageHook          = myManageHook  -- Добавили ХУК Сюда
        , normalBorderColor  = "black"
        , focusedBorderColor  = "orange"   
	}

-- Добавим хуук с реализацией
myManageHook :: ManageHook
	
myManageHook = composeAll . concat $
	[ [className =? c --> doF (W.shift "1:web")		| c <- myWeb]
	, [className =? c --> doF (W.shift "2:dev")		| c <- myDev]
	, [className =? c --> doF (W.shift "3:term")	        | c <- myTerm]
	, [className =? c --> doF (W.shift "4:vm")		| c <- myVMs]
	, [manageDocks]
	]
	where
	myWeb = ["Firefox","Chromium","Chrome"]
	myDev = ["Eclipse","Gedit","sublime-text"]
	myTerm = ["Terminator","xterm","urxvt"]
	myVMs = ["VirtualBox"]
....

Теперь


xmonad --recompile && xmonad --restart

Через dmenu запускаем например sublime-text, и вот она! Вот она магия! Он сразу оказался на рабочем столе с пометкой 2:dev.

Нажмём modKey+shift+enter для запуска терминала и, о чудо! Он окажется на 3-м рабочем столе.

А как же мне узнать className тайла для отправки на нужный рабочий стол?

Это очень просто. Открываем консоль, набираем команду xprop, дальше тыркаем мышкой в нужное окно и в консоле отобразится информация о нём. Нас интересует поле WM_CLASS(STRING).
Впринципе, не обязательно именно по className фильтровать окна, можно и по другим параметрам. Можно увидеть, что, например, по полю title так же можно отправить окошко куда душе угодно.
Это то, к чему мы так долго шли…
И тут мы вспомнили о дяде Фёдоре. Точнее — о xmobar. Давайте сделаем статус бар. Для тех, кто ещё не понял, это чёрная панелька сверху (скриншот в шапке).

Шаг 6: Напильник

Итак, xmobar уже должен быть установлен. Поэтому открываем наш конфиг xmonad.hs и меняем:

main = do
        xmonad defaults

На:

main = do
	xmproc <- spawnPipe "/usr/bin/xmobar ~/.xmonad/xmobar.hs"
	xmonad $ defaults {
	logHook =  dynamicLogWithPP $ defaultPP {
            ppOutput = System.IO.hPutStrLn xmproc
          , ppTitle = xmobarColor xmobarTitleColor "" . shorten 100
          , ppCurrent = xmobarColor xmobarCurrentWorkspaceColor "" . wrap "[" "]"
          , ppSep = "   "
          , ppWsSep = " "
          , ppLayout  = ( x -> case x of
              "Spacing 6 Mosaic"                      -> "[:]"
              "Spacing 6 Mirror Tall"                 -> "[M]"
              "Spacing 6 Hinted Tabbed Simplest"      -> "[T]"
              "Spacing 6 Full"                        -> "[ ]"
              _                                       -> x )
          , ppHiddenNoWindows = showNamedWorkspaces
      } 
} where showNamedWorkspaces wsId = if any (`elem` wsId) ['a'..'z']
                                       then pad wsId
                                       else ""

Нет времени ничего объяснять, просто создаём дальше конфиги, а именно ~./xmonad/xmobar.hs:

Config { 
    font = "xft:Droid Sans Mono:size=9:bold:antialias=true"
    bgColor = "#000000",
    fgColor = "#ffffff",
    position = Static { xpos = 0, ypos = 0, width = 1920, height = 16 },
    lowerOnStart = True,
    commands = [
         Run Weather "UUDD" ["-t","<tempC>°C","-L","18","-H","25","--normal","green","--high","red","--low","lightblue"] 36000
        ,Run Memory ["-t","<used>/<total>M (<cache>M)","-H","8192","-L","4096","-h","#FFB6B0","-l","#CEFFAC","-n","#FFFFCC"] 10        
        ,Run Network "enp6s0" [
             "-t"    ,"rx:<rx>, tx:<tx>"
            ,"-H"   ,"200"
            ,"-L"   ,"10"
            ,"-h"   ,"#FFB6B0"
            ,"-l"   ,"#CEFFAC"
            ,"-n"   ,"#FFFFCC"
            , "-c"  , " "
            , "-w"  , "2"
            ] 10
        ,Run Date "%Y.%m.%d %H:%M:%S" "date" 10
        ,Run MultiCpu [ "--template" , "<autototal>"
            , "--Low"      , "50"         -- units: %
            , "--High"     , "85"         -- units: %
            , "--low"      , "gray"
            , "--normal"   , "darkorange"
            , "--high"     , "darkred"
            , "-c"         , " "
            , "-w"         , "3"
        ] 10
        ,Run CoreTemp [ "--template" , "<core0> <core1> <core2> <core3> <core4>°C"
            , "--Low"      , "70"        -- units: °C
            , "--High"     , "80"        -- units: °C
            , "--low"      , "darkgreen"
            , "--normal"   , "darkorange"
            , "--high"     , "darkred"
        ] 50
        ,Run StdinReader
    ],
    sepChar = "%",
    alignSep = "}{",
    template = "%StdinReader% }{ %coretemp% | %multicpu% | %memory%  | %enp6s0% | %UUDD% | <fc=#FFFFCC>%date%</fc>   "
}

Ух, теперь можно присесть и выполнить уже знакомую команду:


xmonad --recompile && xmonad --restart

Должен был появится наш долгожданный бар и теперь, %USERNAME%, должен наконец увидеть, на каком он рабочем сталое находится. Так же вынесли заголовок текущего окна в наш бар (прям как в mac os x, только гибче). За это отвечает dynamicLogWithPP, который копирует информацию в stdin xmobar, предварительно форматируя её.

Очень подробно о xmobar и его плагинах тут.

Будьте внимательны, в font нужно указать существущий шрифт в вашей системе. Его можно найти в выводе команды fc-list.

Шаг 7: Надфиль

Вроде всё работает, но как-то не уютно. Чего бы ещё такого сделать? Давайте отобразим Volume Level в нашем баре! Хм, ХМобаре! Сказал — делай.

Изменяем ~/.xmonad/xmobar.hs

Добрые люди уже написали для нас плагин PipeReader, который тупо вычитывает pipe и отображает это дело в баре.
Добавим вызов плагина PipeReader и изменим шаблон добавив в него отображение громкости:

,Run PipeReader "/tmp/.volume-pipe" "vol"
template = "%StdinReader% }{ %vol% | %coretemp% | %multicpu% | %memory%  | %enp6s0% | %UUDD% | <fc=#FFFFCC>%date%</fc>   "

Создадим pipe

export _volume_pipe=/tmp/.volume-pipe
[[ -S $_volume_pipe ]] || mkfifo $_volume_pipe

Теперь у нас есть pipe, сливаем в него нужные данные. Попробуем узнать уровень громкости следующей командой:

amixer sget Master | grep -o -m 1 '[[:digit:]]*%' | tr -d '%'<source>
Отлично, вот его и "сольём":

<source lang="bash">echo `amixer sget Master | grep -o -m 1 '[[:digit:]]*%' | tr -d '%'`>/tmp/.volume-pipe<source>

Обновленные данные отобразились в xmobar.. Вё равно как то счастья не видно. Может дело в этих сухих цифрах? Так давайте сделаем бар громкости в xmobar! Ух, повеселело сразу.

Создаем скрипт ./xmonad/getvolume.sh:

<source lang="bash">#!/bin/sh
vol=`amixer sget Master | grep -o -m 1 '[[:digit:]]*%' | tr -d '%'`
level=`expr $vol / 10`
bars=$level

case $bars in
  0)  bar='[----------]' ;;
  1)  bar='[|---------]' ;;
  2)  bar='[||--------]' ;;
  3)  bar='[|||-------]' ;;
  4)  bar='[||||------]' ;;
  5)  bar='[|||||-----]' ;;
  6)  bar='[||||||----]' ;;
  7)  bar='[|||||||---]' ;;
  8)  bar='[||||||||--]' ;;
  9)  bar='[|||||||||-]' ;;
  10) bar='[||||||||||]' ;;
  *)  bar='[----!!----]' ;;
esac

echo $bar

exit 0

Примерно так, зато быстро. Проверяем:

echo `~/.xmonad/getvolume.sh`>/tmp/.volume-pipe

Не знаю как у вас, а у меня работает. Но как то оно… не автоматически. Хочется ещё вкусняшек, а если хочется, зачем останавливаться?

Бонусные вкусняшки

Редактируем наш ~/.xmonad/xmonad.hs.

Добавим следующее:

defaults = defaultConfig {
...
	}`additionalKeys` myKeys  -- добавили к дефолтной конфигурации новые кнпоки!
myKeys = [         
         ((mod4Mask, xK_g), goToSelected defaultGSConfig)
	 	     , ((mod4Mask, xK_s), spawnSelected defaultGSConfig ["chromium","idea","gvim"])
	 	     , ((mod4Mask, xK_KP_Add), spawn "amixer set Master 10%+ && ~/.xmonad/getvolume.sh >> /tmp/.volume-pipe")
	 	     , ((mod4Mask, xK_KP_Subtract), spawn "amixer set Master 10%- && ~/.xmonad/getvolume.sh >> /tmp/.volume-pipe")         
         ]

Теперь при нажатии на modKey+ -/+ (это — и + на NumKeyboard) будет изменяться громкость и её уровень сразу отобразится в нашей панельке.
Ещё не уснувший читатель мог заметить дополнительную вкусняшку в виде строчки:

((mod4Mask, xK_s), spawnSelected defaultGSConfig ["chromium","idea","gvim"])

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

Заключение

Хочу лишь сказать, что я не претендую на какую либо исключительность этого материала. Да и правильным его не назовешь, наверняка всё можно сделать лучше. Я всего лишь хотел показать путь, по которому можно пойти для настройки собственного окружения так, чтобы этой холодной осенью на душе было тепло и приятно.

Надеюсь, было интересно и полезно, с радостью обсужу тематику рабочего окружения, xmonad и всего, что связанно с Linux.

PS: Для экономии места все примеры конфигов не снабжены необходимыми директивами import. Импорты можно стянуть с гитхаба.

Материалы

Сайт XMonad — много инфы и доки по API;
Xmobar Reference — дока по Xmobar и его плагинам;
Архив конфигураций XMonad — примеры кофнигов с прилагающимися картинками результата;
XMonad hotkey cheatsheet — все хоткеи на одной страничке. Для начала советую feh --bg-center www.haskell.org/wikiupload/b/b8/Xmbindings.png;
GitHub xmonad-config — Все конфиги, которые тут использовал, в кучку собрал да на гитхабе разместил.

Автор: tolkkv

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js