Понимание Xwayland

Из грязи в князи
Очень долгое время X.Org Server не обновлялся, но недавно я нашел ему замену, и о том, как оно работает я сделаю перевод сразу первой и второй частей о том, что такое Xwayland

Часть I

В первой части данной статьи для моего текущего проекта Google Summer of Code (GSoC) на этой неделе я планировал написать об основной идее проекта, но я пересмотрел и решил сначала дать обзор о том, как Xwayland функционирует на высоком уровне, а в следующей, второй части подробно рассмотреть его внутреннюю работу. Причина этого в том, что сейчас в Xwayland не так уж много документации. Таким образом, эта статья призвана заполнить этот пробел, чтобы протянуть руку помощи заинтересованным новичкам. А через две недели я догоню, как объясню идею проекта.

Когда мы поднимаемся на высокий уровень на этой неделе, первый вопрос заключается в том, чего вообще должен достичь Xwayland? Ты можешь знать это. Во время сессии Wayland это означает, что приложения, которые не поддерживают Wayland, а только старый Xserver, продолжают нормально работать, т.е. обеспечивают обратную совместимость. Но как это делается? Прежде чем мы поговорим об этом, есть еще одна вещь, о которой нужно поговорить, так как раньше я называл Xwayland только чем-то. Что такое Xwayland? Как вам кажется, как это выглядит на вашей Linux-системе? На следующей неделе мы увидим, что ответить на этот вопрос не так просто, как кажется, но пока этого достаточно: это один двоичный файл, содержащий Xserver со специальным бэкендом, написанным для связи с композитором Wayland, работающим в вашей системе - например, с KWin во время сессии Plasma Wayland.

Чтобы сделать его более ощутимым, давайте взглянем на Debian: Существует пакет под названием Xwayland и он состоит в основном только из вышеупомянутого двоичного файла. Этот двоичный файл будет скопирован в /usr/bin/Xwayland. Сравните это с обычным Xserver, предоставляемым X.org, который вы можете найти в Debian в пакете xserver-xorg-core. Соответствующий двоичный файл помещается в /usr/bin/Xorg вместе с символической ссылкой /usr/bin/X, указывающей на него.

В то время как последний является центральным структурным элементом X-сессии и поэтому запускается раньше всего остального с графическим выводом, Xserver в Xwayland работает по-другому: Он встроен в сессию Wayland. А на сессии в Wayland композитор Wayland является центральным звеном. Это означает, в частности, что композитор Wayland также берет на себя роль сервера, который взаимодействует с приложениями Wayland с графическим выводом в качестве своих клиентов. Они посылают ему запрос, чтобы представить свои нарисованные вещи на экране. Xserver в Xwayland binary является лишь необходимым связующим звеном между приложениями, которые могут общаться только с Xserver, и композитором/сервером Wayland. Поэтому двоичный файл Xwayland запускается позже композитором или каким-либо другим способом в рабочем пространстве. В Plasma он запускается компанией KWin после инициализации композитором процесса рендеринга. Соответствующий код вы найдете здесь.

Хотя в данном случае KWin также устанавливает некоторые каналы связи с недавно созданным процессом Xwayland, в целом, связь между Xwayland и сервером Wayland осуществляется по обычному протоколу Wayland Protocoll, аналогично тому, как другие приложения Wayland взаимодействуют с композитором/сервером. Это означает, что окна, запрашиваемые, возможно, несколькими приложениями на базе X и предоставляемые Xwayland в качестве Xserver, переводятся одновременно Xwayland to Wayland совместимыми объектами и, действуя как родной клиент Wayland, отправляются композитору Wayland по протоколу Wayland. Эти окна выглядят для композитора Wayland точно так же, как окна - в терминологии Wayland поверхностей - каждого другого приложения Wayland native. При прочтении этой статьи следует помнить, что приложение в Wayland не ограничивается использованием только одного окна/поверхности, но может создавать несколько одновременно, поэтому Xwayland как родной клиент Wayland может делать то же самое для всех окон, созданных для всех его X клиентов.

Во второй части следующей недели мы внимательно рассмотрим код Xwayland, чтобы увидеть, как Xwayland выполняет свою роль Xserver в отношении своих клиентов на базе X и в то же время выступает в качестве клиента Wayland перед композитором Wayland.


Часть II

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

Вы можете найти исходный код Xwayland здесь. Может быть, к вашему удивлению, это просто код Xserver's X.org's, который мы будем называть Xserver в остальной части этого текста. Но в качестве напоминания из первой части: Xwayland — это всего лишь обычный Xserver "со специальным бэкендом, написанным для связи с композитором Wayland, работающим в вашей системе". Этот бэкенд расположен в /hw/xwayland. Чтобы понять, почему мы находим здесь этот специальный бэкенд и что я имею в виду под бэкендом Xserver, мы должны сначала изучить некоторые основы Xserver.

DIX и DDX

Подкаталог hw - это элемент X (DDX), зависящий от устройства, входящий в состав Xserver. Все остальные каталоги в дереве исходников образуют часть Device Independent X (DIX). Это структурирование является важной абстракцией в Xserver. Как имена говорят о том, что часть DIX должна быть достаточно общей, чтобы быть одинаковой на каждой воображаемой аппаратной платформе. Слово "hardware" следует понимать абстрактно как своего рода среду, в которой работает Xserver и с которой он должен общаться, а именно ядро с его DRM-подсистемой и драйверами оборудования или, как мы уже знаем, композитора Wayland. С другой стороны, весь код, который потенциально отличается от того, для чего скомпилирован Xserver, поставляется в DDX часть. Так как этот код по определению отвечает за создание и поддержание необходимых каналов связи со средой, мы можем назвать специфические для платформы кодовые пути в DDX бэкендами сервера Xserver.

Я хочу подчеркнуть, что Xserver компилируется для различных сред, потому что теперь мы можем понять, как двоичные файлы Xorg и Xwayland, о которых мы говорили в первой части, и что оба реализуют полный Xserver, появились: Autotools, система сборки Xserver, получает информацию о параметрах конфигурации перед компиляцией целевых платформ. Затем он будет использовать для каждой включенной целевой платформы соответствующий подкаталог в hw для компиляции двоичного файла с соответствующим DDX этой платформы плюс общий DIX из других директорий верхнего уровня. Например, чтобы скомпилировать только двоичный файл Xwayland, вы можете использовать эту команду из корня дерева исходных текстов:

./autogen.sh --prefix=/usr --disable-docs --disable-devel-docs \
  --enable-xwayland --disable-xorg --disable-xvfb --disable-xnest \
  --disable-xquartz --disable-xwin

Возвращаясь к функциональности, давайте рассмотрим два примера, чтобы лучше понять разделение DIX и DDX и то, как эти две части взаимодействуют друг с другом. Сначала рассмотрим концепцию регионов: Регион определяет определенную часть представления, отображаемого пользователю. Он определяется значениями его ширины, высоты и положения в некоторой системе координат. Таким образом, работа регионов полностью независима от выбора аппаратного обеспечения, на котором работает Xserver. Это позволило создателям Xserver поместить весь код региона в DIX часть сервера.

Говоря о регионах в представлении, мы думаем, что непосредственно на экране отображается это представление. Это второй пример. Мы всегда можем предположить, что существует какой-то реальный или эмулируемый экран или даже несколько из них для отображения нашего взгляда. Но то, как эти экраны и их свойства восстанавливаются, зависит от окружающей среды. Так что в DDX должен быть какой-то "код экрана", но с другой стороны, мы хотим переместить как можно больше логики в DIX, чтобы избежать переписывания разделяемых функций для различных платформ.

Xserver оснащен инструментами, облегчающими эту дихотомию. В нашем примере об экранах DIX представляет собой общую часть такого экрана в своей _Screen-структуре. Но в структуре также присутствует указатель пустоты devPrivate, который может быть установлен частью DDX на некоторую структуру, которая затем предоставляет устройству зависимую информацию для экрана. Когда DIX затем вызывает DDX для выполнения каких-либо действий, касающихся экрана, DIX также передает указатель _Screen, и DDX может получить эту информацию через указатель devPrivate. Указатель частного ресурса - это инструмент, присутствующий в нескольких основных объектах Xserver. Например, мы также можем найти его в структуре _Window для окон.

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

Xwayland DDX

Имена исходных файлов в каталоге /hw/xwayland уже указывают на то, что они должны делать. К счастью, их не так уж и много, и большинство файлов довольно компактны. Весьма примечательно, что создатели Xwayland смогли обеспечить обратную совместимость X в сессии Wayland, добавив лишь несколько строк кода к общей части обычного Xserver. Разумеется, это возможно только благодаря вышеописанным абстракциям.

Но возвращаясь к файлам, мы получаем таблицу всех файлов с краткими описаниями:

Описание структуры папки /hw/xwayland

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

Некоторые базовые структуры и функции, совместно используемые с другими исходными файлами, определены в заголовочном файле xwayland.h. Хорошо, что все структуры и функции с именами, начинающимися на xwl_, известны только Xwayland DDX и больше не будут вызываться ни откуда. Но в начале файла xwayland.c мы находим некоторые методы без префикса. Они определены только в DIX и их реализация необходима для того, чтобы Xwayland стал полностью функциональным DDX.

Прокручивая вниз до конца файла, мы видим главную точку входа в DDX при запуске сервера, метод InitOutput. Если внимательно присмотреться, вы заметите вызов AddScreen, где мы также подключаем функцию инициализации внутреннего экрана Xwayland в качестве одного из ее аргументов. Но он называется только один раз! Так что насчет нескольких экранов? Объяснение заключается в том, что Xwayland использует расширение RandR для управления экраном и здесь просит только создать одну структуру экрана в виде манекена, который содержит глобальную информацию об окружающей среде Wayland во время работы. Мы рассматривали эту конкретную структуру экрана в предыдущей главе как пример обмена информацией между DIX и DDX через указатели void, и что эти указатели задаются DDX.

Хотя это всего лишь манекен, мы все еще можем следить за этим в действии в подключенной функции init xwl_screen_init. Здесь мы устанавливаем с помощью некоторых методов DIX хэш-ключ для последующей идентификации поля данных, а затем устанавливаем данные, представляющие собой структуру xwl_screen со статической информацией о среде Wayland, в которой установлен сервер Xwayland.

В подключенной функции init также весьма интересна последующая манипуляция указателями функций RealizeWindow, UnrealizeWindow и так далее. Я спросил Дэниела об этом, потому что я не понимал всех шагов, сделанных здесь, а также аналогичных им позже в задействованных функциях xwl_realize_window, xwl_unrealize_window и так далее. Дэниел хорошо объяснил мне механизм, и он действительно очень ловкий. В основном, благодаря этому трюку, называемому обертыванием, Xwayland и другие DDX могут перехватывать вызовы DIX к такой процедуре, как RealizeWindow, выполнять свой собственный код, а затем продолжать процедуру, смотря на DIX так, как будто этого никогда не было.

В случае RealizeWindow, вызываемого при создании окна и готового к отображению, мы перехватываем его с помощью xwl_realize_window, где Xwayland внутреннее представление структуры типа xwl_window выделено со всей специфической для Xwayland дополнительной информацией, в частности, с поверхностью Wayland. В конце запрос на создание поверхности посылается на сервер Wayland по протоколу Wayland. Вы, вероятно, можете представить себе, что должны делать UnrealizeWindow и обернутое xwl_unrealize_window, и что он делает это очень похожим образом.

В качестве последнего пункта давайте рассмотрим цикл обработки событий и отправку буфера возможно нового или измененного графического содержимого. У нас есть блок_обработчик, который был зарегистрирован в Xwl_screen_init в DIX и получает непрерывный вызов в течение всего цикла обработки событий. Отсюда мы вызываем глобальную функцию отправки сообщений о повреждениях и оттуда для каждого окна в xwl_window_post_damage. Если нам повезет, что мы получим буфер с аппаратным ускорением от реализации в xwayland-glamor.c или без него в xwayland-shm.c, прикрепите его к поверхности и выстрелите. В следующем цикле событий мы играем в одну и ту же игру.

Завершая статью, мы полностью проигнорировали обработку входных данных в Xwayland, и мы также коснулись только графического буфера в конце. Но, по крайней мере, графические буферы, которые мы будем подробно обсуждать в ближайшие недели, поскольку мой проект Google Summer of Code посвящен именно этим маленьким ребятам.