# Автоматический тайлинг в Sway
Difrex(tavern,23) — All
2019-04-07 15:08:45


Я давно использую i3wm в работе, а после выхода версии 1.0 Sway я перешел на него с i3. Sway - это пракатически полностью совместимый с i3 композитор Wayland. По-этому перейти на него оказалось очень просто. Мои конфиги Sway можно посмотреть на Github.

Почему-то я долгое время думал, что мне в i3/sway не хватает полностью ручного тайлинга, я пробовал различные оконные менеджеры с ручным тайлингом, такие как, `bspwm`, `herbstlutfwm` и другие, но они не заходили. Потом я решил попробовать AwesomeWM, все-таки это по сути фреймворк и на Lua можно написать все, что угодно. В нем мне очень понравилась стандартная возможность автоматического тайлинга. Вот, что мне переодически нужно, подумал я. Но во всех WM, что я пробовал не было нормальной поддержки скретчпадов - это такие плавающие окна, которые большую часть времени скрыты, и показать их можно, например по комбинации клавишь. Что же мне на самом деле хотелось от WM - скретчпады, полуручной и автоматический тайлинг, возможность менять поведение скриптами на любом языке. По всем параметрам подходил Sway за исключением автоматического тайлинга. Было решено добавить его самостоятельно.

После небольшого иследования этого вопроса оказалось, что у sway есть IPC с возможностью подписки на определенные события. Проблема возникла в том, что существующие биндинги к i3 ipc не подходили, т.к. формат дерева в JSON у Sway отличается, а так же добавляются новые возможности, типа, получения устройств ввода, чего не было в i3.

Так я решил написать(и написал) биндинг к Sway IPC на Go. И уже поверх этого начал писать демона, который реализует автоматический тайлинг.

На данный момент я реализовал `spiral` layout, который работает, так как я хочу и полностью меня устраивает. Так же на начальном этапе реализован `left` layout. Демонстрацию этих режимов можно посмотреть в видео ниже.

## Как этим воспользоваться?

Для начала нужно скомпилировать swaymgr. Из зависимостей только Go - поставь его из репозиториев твоего дистрибутива.


git clone https://github.com/Difrex/gosway
cd gosway/swaymgr
go get -t -v ./...
go build -o ~/.local/bin/swaymgr


Теперь можно добавить swaymgr в конфигурацию sway. Добавь следующие строчки в `~/.config/sway/config`


exec_always ~/.local/bin/swaymgr
bindsym $mod+Alt+m exec ~/.local/bin/swaymgr -s 'set manual'
bindsym $mod+Alt+l exec ~/.local/bin/swaymgr -s 'set left'
bindsym $mod+Alt+s exec ~/.local/bin/swaymgr -s 'set spiral'


Комбинации клавишь меняй по своему вкусу.

Swaymgr запоминает свою расскладку для каждого рабочего стола, а состояние хранит в `~/.autotiling.bolt`. В репозитории ты так же найдешь скрипт, который можно использовать, например, с `i3blocks`.

## Про внутреннее устройство

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

Swaymgr - это отлельное приложение, которое использует `gosway/ipc`.

Для того, чтобы взаимодействовать со Sway через unix-socket необходимо два подключения:

1. Соединение для передачи различных комманд
2. Соединение на которое будут приниматься события от оконного менеджера

Так же для хранения настроек рабочих столов используется встроенная, минималистичная база данных Bolt. Функция `newManager() (*manager, error)` создает все необходимые подключения и вызывает функцию инициализации интерфейсов `Layout`. Возвращаемая структура имеет такой вид:


type manager struct {
commandConn *ipc.SwayConnection
listenerConn *ipc.SwayConnection
store *store
layouts map[string]Layout
}


Главный интерфейс, с помощью которого реализуются все режимы - Layout, выглядит он так: Файл `swaymgr/layouts.go`


type Layout interface {
// PlaceWindow must receive an *ipc.Event
// and do the container manipulation
PlaceWindow(*ipc.Event) error
// Manage must store WorkspaceConfig in the database with
// the workspace name, layout name and with the Managed: true
Manage() error
}


У этого интерфейса есть всего два метода:

* `Manage() error` - вызывается тогда, когда текущий рабочий стол переключается в какой-либо режим
* `PlaceWindow(*ipc.Event) error` - вызывается тогда, когда создается новый контейнер на рабочем столе, который является управляемым.

### Как работает spiral layout

Я рассмотрю управлене окнами на примере реализации режима spiral.

Структура этого режима состоит всего из двух полей:

* `Conn *ipc.SwayConnection` - Коммандное подключение к unix-сокету Sway
* `store *store` - Открытое соединение к базе данных, в котой хранятся текущие настройки рабочих столов

При наступлении события создания нового окна, и если текущий рабочий стол управляется, данное событие передается в метод `PlaceWindow(event *ipc.Event)`. Для начала необходимо найти текущее окно на котором находится фокус:


nodes, err := s.Conn.GetFocusedWorkspaceWindows()
if err != nil {
return err
}
var result ipc.Node
for _, node := range nodes {
if node.Focused {
result = node
break
}
}


Далее вся логика позиционированния окон помещается в 7 строчек:


if result.WindowRect.Width > result.WindowRect.Height {
_, err := s.Conn.RunSwayCommand(fmt.Sprintf("[con_id=%d] split h", event.Container.ID))
return err
} else {
_, err := s.Conn.RunSwayCommand(fmt.Sprintf("[con_id=%d] split v", event.Container.ID))
return err
}


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

## Зарезервированные режимы

Я заранее зарезервировал некоторые режимы которые очень хочется реализовать. Вот они:

* `type FiberLayout struct{}` - режим подсмотренный в AwesomeWM. Окна размещаются симметрично друг под другом и вдоль
* `type TopLayout struct{}` - самое большое окно размещается вверху экрана, остальные внизу разделяясь по горизонтали
* `type BottomLayout struct{}` - тоже самое, что и для TopLayout, но главное окно находится внизу
* `type RightLayout struct{}` - тоже самое, что и LeftLayout, но главное окно размещается справа

Проект на Github. Учитывай, что это пока самая ранняя реализация и тут могут быть баги в больших колличествах, правда ничего критичного я пока не находил - пользоваться можно. Буду очень рад баг-репортам и фич-реквестам, а так же особенно пулл-реквестам. Лицензия проекта: Apache.

Ссылка: https://difrex.lessmore.pw/post/sway-autotiling/