Wayland

Данная статья ни в коем случае не является попыткой точно описать тонкости работы Wayland. Она скорее является сжатым введением и хранилищем ссылок на документацию по теме.

Содержание

Общее описание

Wayland — протокол, с помощью которого описывается взаимодействие GUI приложений. Так же под термином Wayland могут иметь ввиду библиотеку, реализующую базовые примитивы для работы этого протокола [1].

Архитектура протокола подробно описана тут [2], упомянем лишь базовые принципы и компоненты.

Композитор — программа, отвечающая за отрисовку окон. Она является сервером, к которому подключаются один или несколько клиентов (GUI-программ которые хотят показывать окна пользователю).

Каждый клиент может зарегистрировать несколько поверхностей (в терминах wayland -- surface). Поверхность можно связать буфером, содержащим пиксели с изображением окна. Связанный с поверхностью буфер клиент передаёт в композитор через разделяемую память (shared memory), так же существуют оптимизации, позволяющие передать буфер через GPU.

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

Композитор отвечает за позиционирование окон на экране и за обработку запросов от клиентов. При начале взаимодействия с клиентом композитор сообщает ему список поддерживаемых интерфейсов.

Интерфейс представляет из себя описание некоторого объекта, с которым могут работать и клиент и сервер.

Например:

  • wl_surface — интерфейс, описывающий порядок работы с "поверхностями".
  • wl_output — интерфейс, описывающий устройства вывода,
  • wl_shm — интерфейс, описывающий порядок передачи данных от клиента на сервер
  • wl_seat — интерфейс, описывающий устройства ввода

Все интерфейсы состоят из следующих компонентов

  • Запрос (request) — функция, которую может вызвать клиент, для этого объекта. Сервер регистрирует обработчик на каждый запрос интерфейса. Эти обработчики вызываются асинхронно, при обращениях клиента.
Пример — метод attach объекта wl_surface (которы присоединяет буфер к surface)
  • Событие (event) — функция, которую может вызвать сервер для этого объекта. Клиент может регистрировать обработчики, которые будут вызываться асинхронно, при наступлении этого события.

Интерфейсы (основные и дополнительные) описываются в специальном xml-файле, который по совместительству является и документацией [5].

Для того, чтобы работать с интерфейсом в c-коде необходимо сгенерировать заголовочные файлы для клиента и сервера с помощью утилиты wayland-scanner.

wayland API

Базовую информацию о том, как писать клиент и сервер настоятельно рекомендуется посмотреть тут [6].

Сервер

Работа начинается с того, что сервер создаёт новый объект типа wl_display. wl_display является глобальным контекстом, который используется при работе с клиентами. Композитору нужен один объект такого типа.

После этого сервер должен создать UNIX-сокет по которому клиенты смогут подключиться к серверу.

1
2
struct wl_display *display = wl_display_create();
char *sockpath = wl_display_add_socket_auto(ctx->display);

Затем серверу необходимо зарегистрировать те интерфейсы и их версии, которые он поддерживает.

1
2
struct wl_compositor *compositor = wl_global_create(display, &wl_compositor_interface,
	3, userdata, &bind_compositor);

При этом wl_compositor_interface -- это структура типа wl_interface, которая содержит базовое описание интерфейса (имя, версия, количество типов запросов, ...). bind_compositor — это функция, которая будет вызвана, когда клиент попытается создать объект с этим интерфейсом.

Основной работой bind_compositor будет создание нового объекта (в терминах wayland -- ресурса), который будет связан с этим клиентом. При создании этого объекта сервер передаёт callback-функции, которые будут обрабатывать запросы клиента.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
static const struct wl_compositor_interface compositor_interface = {
	.create_surface = compositor_create_surface,
	.create_region = compositor_create_region
};
static void bind_compositor(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
	struct wl_resource *resource = wl_resource_create(client, &wl_compositor_interface,
				      version, id);

	wl_resource_set_implementation(resource, &compositor_interface, data, NULL);
}

Помимо функций для обработки запросов wl_resource_set_implementation позволяет связать с ресурсом произвольные пользовательские данные.

Подобным образом происходит работа со всеми интерфейсами в wayland. Сервер регистрирует обработчики запросов на каждый ресурс и в них реализует всю логику работы приложения.

Структура библиотеки:

  • структуры данных wl_list, wl_array, ...
  • петля событий
  • таймеры
  • wayland-сигналы — примитив, позволяющий доставлять асинхронные нотификации при возникновении произвольных событий

Ссылки

  1. https://github.com/wayland-project/wayland
  2. https://wayland.freedesktop.org/architecture.html
  3. https://drewdevault.com/2017/06/10/Introduction-to-Wayland.html
  4. https://habr.com/en/post/322580/
  5. https://github.com/wayland-project/wayland/blob/master/protocol/wayland.xml
  6. https://jan.newmarch.name/Wayland/ProgrammingClient/
  7. https://github.com/dzruyk/amcs-wm