Автоматический тайлинг в Sway
Apr 6, 2019Я давно использую 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 необходимо два подключения:
- Соединение для передачи различных комманд
- Соединение на которое будут приниматься события от оконного менеджера
Так же для хранения настроек рабочих столов используется встроенная, минималистичная база данных 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-сокету Swaystore *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.