IT Notes

Первое знакомство с dwm

Динамические оконные менеджеры для меня все еще остаются довольно непривычными. Отталкивает сама концепция их работы. Мои рассуждения на эту тему можно найти в заметке Почему я пользуюсь Openbox?. Но все же дадим шанс еще одному их представителю - dwm.

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

Далее я расскажу о своих впечатлениях от dwm. Укажу некоторые его особенности. И приведу небольшие примеры конфигураций, которые успел попробовать.

Первый запуск dwm

После мгновенной установки и запуска dwm я увидел следующее:

dwm-first-start-thumbnail

В отличие от Openbox в верхней части экрана расположена стандартная панель. Только вот больше ничего и нет. Никаких меню или чего-то подобного dwm не предоставляет.

В панели мы видим цифры от 1 до 9. Они соответствуют девяти виртуальным рабочим столам. В документации dwm их называют тэгами (tags). Само собой, мы можем переключаться между ними. Либо с помощью мыши, либо клавишами Mod+N, где Mod - клавиша-модификатор (по умолчанию Alt), а N - номер тэга.

Также можно нажать Mod+0. Это приведет к объединению содержимого всех тэгов. При этом окна запущенных приложений будут умещены на одном экране.

Первая интересная особенность dwm в том, что для каждого монитора тэги независимы. Например, на 1-ом мониторе я могу перейти на 3-ий тэг. В это время на 2-ом мониторе ничего не изменится:

dwm-two-monitors-tags-thumbnail

После тэгов идет обозначение текущего режима компоновки. Доступно три режима на выбор: tiled ([]=), floating (><>) и monocle ([N]). О них мы поговорим чуть позже, когда научимся запускать приложения :)

Интересно, что режим компоновки распространяется сразу на все тэги. Но вновь каждый монитор живет своей жизнью. Например, на 1-ом можно выбрать тайловый режим, а на другом плавающий.

Вслед за режимом компоновки на панели отведено место для вывода заголовка активного окна. А в самом правом краю панели находится еще одно небольшое поле. В нем можно выводить полезную информацию. По сути, своеобразная встроенная альтернатива Conky. Скоро мы посмотрим на эту возможность поближе.

Запуск приложений в dwm

Для любого пользователя Linux основным приложением, конечно, является терминал. В dwm по умолчанию предусмотрена комбинация клавиш Mod+Shift+Enter для его запуска. Я уже давно привык к sakura, поэтому появившийся st не вызвал у меня большого энтузиазма. Но сменить используемый терминал не сложно, т.е. проблемой это считать нельзя.

Другая встроенная возможность dwm заключается в использовании лаунчера dmenu. Этот пакет нужно устанавливать отдельно. Во многом он напоминает gmrun, который я привык использовать в Openbox. Вызывается dmenu с помощью комбинации клавиш Mod+P. Выглядит это следующим образом:

dwm-dmenu-thumbnail

В целом этого уже достаточно, чтобы хоть как-то можно было пользоваться системой. Но давайте посмотрим, что готов предложить dwm для повышения нашей эффективности.

Варианты компоновки и управление окнами в dwm

Начнем с главной фишки dwm.

Тайловый режим компоновки dwm

Этот режим используется в dwm по умолчанию. Однако его можно включить принудительно с помощью комбинации клавиш Mod+T.

Запустим сразу 5 терминалов, и посмотрим, что из этого получилось:

dwm-tiled-1-4-thumbnail

Экран условно поделен по вертикали на две части. Слева - главная область (в документации называется master), а справа - все остальное (называется стэком). Мы можем увеличивать и уменьшать вместимость главной области с помощью клавиш Mod+I и Mod+D соответственно. Например, увеличим вместимость до трех окон (нужно два раза нажать Mod+I):

dwm-tiled-3-2-thumbnail

Переключаться между окнами в текущем тэге можно с помощью клавиш Mod+J и Mod+K. Первая комбинация отвечает за перемещение вниз, а вторая - вверх. Если нажать Mod+Enter, то окно в фокусе перемещается на самый верх. Верхом считается первое окно в главной области. Если ее вместимость сделать равной единице, то окно будет занимать всю левую часть экрана. Похожий смысл эти клавиши имеют и в других режимах.

Также можно изменять размеры областей. Для этого предусмотрены клавиши Mod+H и Mod+L. Первая из них уменьшает главную область, а вторая увеличивает. Все это происходит за счет изменения ширины стэка. Например, нажмем пару раз Mod+H:

dwm-tiled-resize-thumbnail

Плавающий режим компоновки dwm

Для перехода в этот режим используйте комбинацию клавиш Mod+F. После его активации окна станут открываться без привязки к сетке, как было в тайловом режиме:

dwm-floating-thumbnail

Окнами можно управлять с помощью мыши при нажатой клавише Mod:

  1. Левая кнопка мыши для перемещения окна;
  2. Правая кнопка мыши для изменения размеров окна;
  3. Средняя кнопка мыши для включения/выключения плавающего режима для окна.

Окна в dwm не имеют рамок, поэтому закрыть приложение можно либо через его собственное меню. Либо с помощью комбинации клавиш Mod+Shift+C. Это относится ко всем режимам.

Плавающий режим можно использовать и для отдельных окон, не меняя общего режима компоновки. Для этого необходимо нажать Mod+Shift+Space. Повторное нажатие указанной комбинации возвращает окно обратно под управление текущего режима. Также можно воспользоваться мышью при нажатой клавише Mod, как описано выше.

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

Стандартных комбинаций клавиш для изменения размеров и положения окна в плавающем режиме при беглом просмотре документации я не увидел. Но для dwm существует много патчей. Думаю, там эти функции должны быть.

Режим компоновки monocle в dwm

Для перехода в этот режим нужно нажать Mod+M. Логика его работы довольно проста. В каждый момент времени отображается только одно окно. Остальные находятся под ним (кроме тех, что "плавают"). Для переключения между окнами можно использовать клавиши Mod+J и Mod+K.

Для общности привожу скриншот:

dwm-monocle-thumbnail

Управление dwm в многоэкранной системе

Как я уже отмечал, все мониторы в dwm живут своей жизнью. Но можно их и подружить. В частности, с помощью мыши плавающие окна переносятся с одного экрана на другой.

Но можно обойтись и без мыши.

Чтобы сделать предыдущий монитор активным, нужно нажать Mod+<. Логично, что для перехода на следующий подойдет комбинация Mod+>. Переход осуществляется по циклу. Хотя если у вас два монитора, как у меня, то обе комбинации делают одно и то же. Т.е. просто переводят фокус с одного монитора на другой.

Для перемещения текущего окна на соседний монитор добавьте к комбинациям из предыдущего абзаца клавишу Shift. Т.е. Mod+Shift+< переносит окно на предыдущий монитор, а Mod+Shift+> - на следующий.

Кстати, подобные комбинации работают и для переноса окон с одного тэга на другой. Например, чтобы активное окно оказалось на 5-ом тэге, нажмите Mod+Shift+5.

Статус-бар в dwm

На панели dwm можно вывести любой текст. Это делается при помощи команды xsetroot. Например, чтобы просто отобразить Hello, world, введите на консоли следующее:

xsetroot -name "Hello, world"

В правом верхнем углу текущего монитора появится соответствующая надпись:

dwm-xsetroot-hello-world-thumbnail

Это, конечно, уже что-то, но нам бы хотелось выводить информацию поинтереснее. С задачей поможет справиться бесконечный цикл:

while true; do
    if [ "$( xset -q | awk -F \" \" '/Group 2/ {print($4)}' )" = "on" ]; then
        LAYOUT="RU";
    else
        LAYOUT="EN";
    fi;

    xsetroot -name "$LAYOUT | Vol: $( ~/.scripts/get_vol.sh ) | $( date +"%T" )"
    sleep 1s
done &

Этот скрипт каждую секунду выводит текущую раскладку, громкость и время:

dwm-xsetroot-info-thumbnail

Часть, касающуюся определения раскладки, я позаимствовал на wiki-странице Archlinux по dwm. Там же вы можете найти примеры скриптов для получения другой интересной системной информации.

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

#!/bin/sh

if [[ $(pactl list sinks | grep -m 1 "Звук выключен:" | cut -d " " -f3) == "yes" ]]; then
    echo 0%
else
    echo $(pactl list sinks | grep -m 1 "Громкость:" | tr -s ' ' | cut -d " " -f5)
fi

При желании вы можете выводить сюда любую интересующую вас информацию.

Чтобы каждый раз не набирать приведенный бесконечный цикл вручную, сохраните его в файл ~/.xprofile. Если вы используете экранный менеджер на подобии LXDM, GDM или KDM, то этого должно хватить.

Через этот же скрипт можно стартовать все приложения, которые должны быть запущены в начале работы с dwm. Не забывайте указывать в конце команд запуска знак &, чтобы процессы запускались в фоновом режиме.

Если вариант с ~/.xprofile не сработал, попробуйте поэкспериментировать с ~/.xinitrc. Добавьте указанный цикл в него.

Если у вас уже чешутся руки, чтобы добавить еще и Conky-панели, то не спешите. Прямой поддержки Conky в dwm нет. Запустить его в стандартном режиме можно, но получите вы явно не то, что ожидали:

dwm-conky-bad-thumbnail

Но есть обходные решения. Одно из них заключается в использовании dzen. Однако сейчас мы на нем останавливаться не будем.

Другое потенциальное решение заключается в использовании (или создании) специального dwm-патча для Conky. Этим вопросом я также пока что не занимался.

Настройка dwm через config.h

Самая хард-корная часть dwm осталась под конец :) Займемся изменением конфигурации и перекомпиляцией этого оконного менеджера.

Подготовительный этап перед настройкой dwm

Я проводил тестирование под Archlinux, поэтому первая часть инструкций относится именно к нему и не подойдет для большинства других дистрибутивов.

Для начала нам понадобится пакет abs. Когда он будет установлен, запустим под root-ом соответствующую команду:

sudo abs

На самом деле, использовать abs не обязательно. Можно просто скачать исходники dwm, и работать с ними. Однако с abs собранные пакеты окажутся под контролем pacman. А это никогда не помешает.

В результате запуска abs появится каталог /var/abs/. В нем есть несколько подкаталогов. В частности, действительным должен быть путь: /var/abs/community/dwm/. Скопируем этот каталог в /var/abs/local/:

sudo cp -R /var/abs/community/dwm/ /var/abs/local/

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

sudo chown -R $USER /var/abs/local/dwm/

В каталоге несколько файлов:

-rw------- 1 michael root 6,0K мар  1 21:17 config.h
-rw------- 1 michael root  103 мар  1 21:17 dwm.desktop
-rw------- 1 michael root  173 мар  1 21:17 dwm.install
-rw------- 1 michael root 1,2K мар  1 21:17 PKGBUILD

Нас интересует config.h. Откроем его. Это обычный заголовочный файл на Си. Пройдемся по основным настройкам, которые в нем можно поменять.

Изменение клавиши-модификатора в dwm

После запуска dwm мне сразу не понравилось, что в качестве клавиши-модификатора Mod используется Alt. Поэтому поменяем ее на Win. Для этого найдем следующую строку:

#define MODKEY Mod1Mask

И заменим ее на эту:

#define MODKEY Mod4Mask

Определение команд

Следующим шагом поменяем стандартный терминал st на sakura. Изменим следующую строку:

static const char *termcmd[]  = { "st", NULL };

Таким образом:

static const char *termcmd[]  = { "sakura", NULL };

Фрагмент выше определяет команду запуска терминала. Она записана в виде массива строк. Конец массива определяется с помощью NULL.

Если при запуске команды требуются дополнительные ключи, то каждый из параметров записывается в виде отдельного элемента массива. Например, добавим команды для получения скриншота и управления громкостью (здесь и дальше я использую абсолютный путь к своему домашнему каталогу, поэтому не забудьте сделать поправки):

/* Команда для получения скриншота: */
static const char *scrotcmd[]  = { "scrot", "/home/michael/screenshots/%Y-%m-%d-%T-screenshot.png", NULL };


/* Команды управления громкостью: */

/* Громкость выше на 5% */
static const char *volupcmd[]  = { "amixer", "-D", "pulse", "sset", "Master", "5%+", "unmute", NULL };

/* Громкость ниже на 5% */
static const char *voldowncmd[]  = { "amixer", "-D", "pulse", "sset", "Master", "5%-", "unmute", NULL };

/* Вкл/выкл звук */
static const char *volmutecmd[]  = { "amixer", "-D", "pulse", "sset", "Master", "toggle", NULL };

Также в качестве эксперимента я добавил команды для быстрого запуска chromium и gvim при помощи скрипта find_app.sh. Его код приводится в одной из статей по настройке Openbox.

А вот и сами команды:

/* Определили путь к скрипту */
#define FIND_APP_SH "/home/michael/.scripts/find_app.sh"

/* Команда запуска chromium: */
static const char *browsercmd[]  = { FIND_APP_SH, "chromium", NULL };

/* Команда запуска gvim: */
static const char *vimcmd[]  = { FIND_APP_SH, "gvim", NULL };

Привязка команд к комбинациям клавиш

Команды готовы, но нужно привязать их к клавишам. Для этого в config.h имеется массив keys:

static Key keys[] = {
	/* modifier                     key        function        argument */
	{ MODKEY,                       XK_p,      spawn,          {.v = dmenucmd } },
	{ MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },

        /* И т.д… */
};

Структура довольно проста. Сначала идут флаги клавиш-модификаторов. Затем основная кнопка для действия. Далее функция dwm. И наконец аргументы функции. Кроме функции spawn есть и другие, но мы их трогать не будем.

Свяжем наши команды со следующими клавишами:

  1. Mod+; - сделать скриншот scrotcmd;
  2. Mod+] - увеличить громкость volupcmd;
  3. Mod+[ - уменьшить громкость voldowncmd;
  4. Mod+Shift+M - Вкл/выкл звук volmutecmd;
  5. Mod+B - запустить браузер browsercmd;
  6. Mod+V - запустить GVim vimcmd.

Констант для кодов клавиш специальных символов в config.h не предусмотрено. Поэтому на помощь приходит утилита xev. Запустите ее в консоли. Затем нажмите нужную клавишу. Например, для ; можно увидеть следующее:

KeyPress event, serial 48, synthetic NO, window 0x4000001,
    root 0x2a3, subw 0x0, time 36945581, (1056,298), root:(1057,334),
    state 0x0, keycode 47 (keysym 0x3b, semicolon), same_screen YES,
    XLookupString gives 1 bytes: (3b) ";"
    XmbLookupString gives 1 bytes: (3b) ";"
    XFilterEvent returns: False

Нам понадобится код 3b. Аналогично можно найти коды и для других специальных клавиш. В том числе и мультимедийных.

Также обращаю внимание, что по умолчанию в dwm на комбинацию Mod+B назначено действие "спрятать/показать панель". Мне эта функция не нужна, поэтому я просто удалил соответствующую строку инициализации:

{ MODKEY,                       XK_b,      togglebar,      {0} },

А вот, что я добавил:

static Key keys[] = {
	/* modifier                     key        function        argument */
        /* … */

	{ MODKEY,                       0x3b,      spawn,          {.v = scrotcmd } },
	{ MODKEY,                       0x5d,      spawn,          {.v = volupcmd } },
	{ MODKEY,                       0x5b,      spawn,          {.v = voldowncmd } },
	{ MODKEY|ShiftMask,             XK_m,      spawn,          {.v = volmutecmd } },
	{ MODKEY,                       XK_b,      spawn,          {.v = browsercmd } },
	{ MODKEY,                       XK_v,      spawn,          {.v = vimcmd } },

        /* … */
};

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

Правила отображения окон

Также не могу пройти мимо настроек специальных правил размещения окон. Они определяются массивом rules:

static const Rule rules[] = {
	/* xprop(1):
	 *	WM_CLASS(STRING) = instance, class
	 *	WM_NAME(STRING) = title
	 */
	/* class      instance    title       tags mask     isfloating   monitor */
	{ "Gimp",     NULL,       NULL,       0,            1,           -1 },
	{ "Firefox",  NULL,       NULL,       1 << 8,       0,           -1 },
};

Для идентификации окна проще всего использовать его класс, который стоит в качестве первого компонента. Кстати, прямо в config.h дается подсказка, как определить значение class для окна. Для этого воспользуйтесь утилитой xprop. В примере конфигурации по умолчанию приводятся классы Gimp и Firefox.

Параметр tags mask задает те тэги, к которым нужно привязать окно. Значение определяется с помощью бинарных флагов. Если указать ноль, то после запуска приложения его окно отобразится на активном тэге. Если указать 1, то на первом. Для отображения на втором подойдет значение 1 << 1. На третьем - 1 << 2. И так до 1 << 8 для девятого. Можно задать сразу несколько тэгов. Например: 1 | 1 << 3 для отображения сразу на 1-ом и 4-ом тэге. Допустимы и любые другие бинарные комбинации.

Следующий параметр isfloating указывает на то, должно ли окно по умолчанию появляться в плавающем режиме. Ноль означает "нет", а все остальные значения - "да". Плавающий режим в качестве режима по умолчанию хорошо подойдет для различных мессенджеров и других многооконных приложений.

Последний параметр monitor позволяет указать экран, на котором должно появляться окно. Значение -1 означает, что будет использован текущий монитор. При 0 окно отобразится на первом мониторе. Для 1 - на втором. Для проверки других значений у меня не хватает мониторов, но логично предположить, что они также должны работать.

В качестве эксперимента добавим пару своих правил:

static const Rule rules[] = {
        /* Стандартные правила убраны для краткости */

	{ "chromium", NULL,       NULL,       1 << 1,       0,           0 },
	{ "Gvim",     NULL,       NULL,       0,            0,           1 },
};

Перекомпиляции dwm с новым config.h

Теперь мы готовы к перекомпиляции dwm. Если вы собирали его из исходников, то просто выполните что-то на подобии make && make install. Я же воспользуюсь преимуществами abs.

Сначала необходимо перейти в каталог /var/abs/local/dwm/. А затем выполнить следующую команду:

updpkgsums && makepkg -fi

Осталось только перезапустить dwm. Завершить сеанс можно с помощью комбинации клавиш Mod+Shift+Q. После повторного входа в dwm наша конфигурация вступает в силу.

Особенности поведения find_app.sh в dwm

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

Для запущенных приложений find_app.sh работает гораздо лучше. После нажатия комбинации клавиш окно не просто получит фокус, но и попадет в главную область тайлового режима.

Выводы

В целом dwm мне понравился. У него есть свои сильные и слабые стороны, к которым можно привыкнуть. Этот оконный менеджер предназначен для истинных ценителей минимализма.

Его мощь заключается в простоте добавления новых функций. Настройка dwm сводится к программированию. Вы сами дописываете код, чтобы получить именно то, что нужно. Также можно воспользоваться готовыми патчами, которые уже кто-то успел написать до вас.

Нельзя сказать, что идея программирования приложения под себя нова. В конце концов, практически для всех Linux-программ доступны исходные коды. Особенность dwm в его компактной архитектуре, предназначенной специально для изменений. При этом я сильно сомневаюсь, что многие пользователи решаются лезть в исходники Gnome или KDE :)

Написанием этой статьи я занимался под dwm. У меня он выглядит примерно так:

dwm-my-desktop-thumbnail

С другой стороны, вряд ли я перейду на него со своего Openbox :)

Свои вопросы и предложения по настройке dwm оставляйте в комментариях под этой статьей.

Похожие публикации

Комментарии

Жду продолжение, очень уж надо настроить. Патчи.

Anonymous:

Жду продолжение, очень уж надо настроить. Патчи.

Какой функционал интересует? Патчей много.

Требую вторую часть!

Ок. К следующему разу попробую настройки на свой вкус.

Интересно!

Ок. К следующему разу попробую настройки на свой вкус

Уступает ли dwm по функционалу i3-wm? Может ли делить таб по вертикали/горизонтали много раз?

Anonymous:

Уступает ли dwm по функционалу i3-wm?

Я не имею большого опыта работы с i3, но судя по доступной информации i3 в базовой поставке представляет собой более мощный оконный менеджер, чем dwm

Anonymous:

Может ли делить таб по вертикали/горизонтали много раз?

Без патчей такой возможности dwm не предоставляет. Однако дополнительные режимы компоновки достаточно легко добавляются

Снова вернулся на i3-wm, все же для меня он идеален. У dwm есть ряд недоработок в плане компоновки, а чтобы их исправить - нужно намного лучше изучить Си и переписать dwm.