X11 это тот механизм на чем работает весь графический интерфейс Unix подобных ОС.


Но мало кто знает как он работает на самом деле. Потому что с годами он оброс слоями и слоями библиотек, которые стремятся скрыть саму сущность протокола.


А протокол в своей сути прекрасен. Он лаконичен и почти совершенен.


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


А все книги и статьи по использованию X11 описывают это через библиотеки прокладки типа XLib и XCB, и даже, что хуже, GTK или Qt.


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


Как бы то ни было, если кому-то интересно как все работает на самом деле, пожалуйста под кат.


Суть


Суть X11 в том, что есть программа сервер (X server) которая ожидает подключения и выполняет те команды которые получает от клиента. Например создать графическое окно. Нарисовать что-то и так далее.


Клиенты подключаются к серверу через обычный сокет. Посылают команды и получают обратно ответы, ошибки, если что-то пошло не так, а также события (например перемещения мыши, нажимания на кнопки и т.п.)


Клиент, по сути это консольная программа, которая с графикой не имеет ничего общего, кроме этого сетевого соединения.


Протокол


Весь основной протокол описан в документе X Window System Protocol


Самое полезное в этом документе, это приложение «B», где описано побайтно что и куда присылается и принимается.


Я буду цитировать отрывки, чтобы иллюстрировать текст.


Идентификаторы


Все объекты в X имеют идентификатор. Это 32 битовое число, которое генерирует клиент и передает серверу, чтобы обозначить создаваемый объект. Например окно, курсор, картинка и т.д.


Другой тип идентификаторов это ATOM. Атомы это тоже 32 битовые числа, но их генерирует сервер. Клиент передает серверу какую-то символьную строку, а сервер в ответ дает число. Одинаковым строкам всегда соответствует одинаковое число. Это похоже на хеширование, но сделано по другому – сервер просто хранит список строк и присваивает им номера. Если какой-то клиент запросит атом для строки которая уже находится в списке, ему возвращают номер строки в списке.


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


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


Чтобы снизить нагрузку на сервер, самые важные атомы определены в стандарте и всегда имеют одни и те же значения. Если кому-то интересно, список здесь:


Стандартные атомы

То что написано большими буквами, является той строкой из которой генерирован атом:


atomPRIMARY            = 1
atomSECONDARY          = 2
atomARC                = 3
atomATOM               = 4
atomBITMAP             = 5
atomCARDINAL           = 6
atomCOLORMAP           = 7
atomCURSOR             = 8
atomCUT_BUFFER0        = 9
atomCUT_BUFFER1        = 10
atomCUT_BUFFER2        = 11
atomCUT_BUFFER3        = 12
atomCUT_BUFFER4        = 13
atomCUT_BUFFER5        = 14
atomCUT_BUFFER6        = 15
atomCUT_BUFFER7        = 16
atomDRAWABLE           = 17
atomFONT               = 18
atomINTEGER            = 19
atomPIXMAP             = 20
atomPOINT              = 21
atomRECTANGLE          = 22
atomRESOURCE_MANAGER   = 23
atomRGB_COLOR_MAP      = 24
atomRGB_BEST_MAP       = 25
atomRGB_BLUE_MAP       = 26
atomRGB_DEFAULT_MAP    = 27
atomRGB_GRAY_MAP       = 28
atomRGB_GREEN_MAP      = 29
atomRGB_RED_MAP        = 30
atomSTRING             = 31
atomVISUALID           = 32
atomWINDOW             = 33
atomWM_COMMAND         = 34
atomWM_HINTS           = 35
atomWM_CLIENT_MACHINE  = 36
atomWM_ICON_NAME       = 37
atomWM_ICON_SIZE       = 38
atomWM_NAME            = 39
atomWM_NORMAL_HINTS    = 40
atomWM_SIZE_HINTS      = 41
atomWM_ZOOM_HINTS      = 42
atomMIN_SPACE          = 43
atomNORM_SPACE         = 44
atomMAX_SPACE          = 45
atomEND_SPACE          = 46
atomSUPERSCRIPT_X      = 47
atomSUPERSCRIPT_Y      = 48
atomSUBSCRIPT_X        = 49
atomSUBSCRIPT_Y        = 50
atomUNDERLINE_POSITION = 51
atomUNDERLINE_THICKNESS= 52
atomSTRIKEOUT_ASCENT   = 53
atomSTRIKEOUT_DESCENT  = 54
atomITALIC_ANGLE       = 55
atomX_HEIGHT           = 56
atomQUAD_WIDTH         = 57
atomWEIGHT             = 58
atomPOINT_SIZE         = 59
atomRESOLUTION         = 60
atomCOPYRIGHT          = 61
atomNOTICE             = 62
atomFONT_NAME          = 63
atomFAMILY_NAME        = 64
atomFULL_NAME          = 65
atomCAP_HEIGHT         = 66
atomWM_CLASS           = 67
atomWM_TRANSIENT_FOR   = 68

Запросы


Все запросы в X11 бинарные, с полями разной длины. По сути, здесь есть поля длиной в 1 байт, 2 байта и 4 байта.


Первые 4 байта запроса всегда присутствуют и всегда содержат одинаковую информацию:


Смещение Длина Содержание
0 1 Код команды. Основной протокол использует только значения от 1 до 127, а значения больше 127 выделены расширениям.
1 1 Подкоманда или какой-то параметр запроса длиной в 1 байт или не используется.
2 2 Длина всего запроса в двойных словах (4 байта).

Прочтя этот заголовок, сервер уже знает сколько байт (а точнее двойных слов) еще надо прочесть чтобы забрать весь запрос.


Чтобы не быть слишком голословным покажу простой пример:


Запрос «DestroyWindow» кодируется вот так (допустим хотим закрыть окно с ID 0x12345678):


Смещение Длина Значение Заметки
0 1 0x03 3 это код операции DestroyWindow
1 1 0x00 Не используется. Значение может быть любое. Сервер все-равно его не смотрит.
2 2 0x0002 Длина запроса 2 двойных слова или 8 байт.
4 4 0x12345678 Идентификатор окна.

Или в итоге, по сокету уходит вот что: 03 00 02 00 78 56 34 12


Получив этот запрос, X сервер закроет окно с идентификатором 0x12345678


В документации протокола (а точнее в приложении), вот это запрос DestroyWindow описан следующим синтаксисом:


     1     4                               opcode
     1                                     unused
     2     2                               request length
     4     WINDOW                          window

А сейчас что-то посложнее: «CreateWindow».


Предварительно надо выбрать идентификатор окна. Выберем опять 0x12345678 чтобы было попроще.
Еще понадобиться идентификатор коренного окна (это служебное окно, которое занимает весь дисплей и является родительским для всех окон верхнего уровня. Допустим его идентификатор 0x9abcdef0 (а откуда взять реальные значения, я расскажу немножко позже).


Смещение Длина Имя поля Значение Заметки
0 1 opcode 0x01 Операция CreateWindow == 1
1 1 depth 0x00 Глубина цвета окна. 0 значит CopyFromParent
2 2 length 0x0008
4 4 wid 0x12345678 Идентификатор который выбрали.
8 4 parent 0x9abcdef0
12 2 x 0x64 Это X координата верхнего левого угла нашего окна.
14 2 y 0x65 Это Y координата окна.
16 2 width 0xc8 Ширина окна.
18 2 height 0x66 Высота окна.
20 2 border 0x0000 Ширина рамки окна.
22 2 class 0x0001 1 это окно InputOutput. Есть и InputOnly, но они слишком специфические (да и не окна по сути) и их не будем рассматривать здесь.
24 4 visual 0x00000000 0 значит скопировать из родителя. Visual это какое-то абстрактное представление экрана в котором я так и не разобрался. Но CopyFromParent работает всегда. ;)
28 4 value_mask 0x00000000 Здесь кончается фиксированная часть запроса. (длиной в 8 двойных словах). Если нужно, можно задать дополнительные параметры окна. Для этого нужно в value_mask поставить единицы в некоторые биты, поставить необходимые параметры после 32го байта запроса и соответственно увеличить длины запроса в поле length на нужное число двойных слов.

И так, итоговый запрос который отправляем на сокет: 01 00 08 00 78 65 43 21 f0 de bc 9a 64 65 c8 66 00 00 01 00 00 00 00 00 00 00 00 00


Вот и полное описание запроса в приложении протокола:


     1     1                               opcode
     1     CARD8                           depth
     2     8+n                             request length
     4     WINDOW                          wid
     4     WINDOW                          parent
     2     INT16                           x
     2     INT16                           y
     2     CARD16                          width
     2     CARD16                          height
     2     CARD16                          border-width
     2                                     class
          0     CopyFromParent
          1     InputOutput
          2     InputOnly
     4     VISUALID                        visual
          0     CopyFromParent
     4     BITMASK                         value-mask (has n bits set to 1)
          #x00000001     background-pixmap
          #x00000002     background-pixel
          #x00000004     border-pixmap
          #x00000008     border-pixel
          #x00000010     bit-gravity
          #x00000020     win-gravity
          #x00000040     backing-store
          #x00000080     backing-planes
          #x00000100     backing-pixel
          #x00000200     override-redirect
          #x00000400     save-under
          #x00000800     event-mask
          #x00001000     do-not-propagate-mask
          #x00002000     colormap
          #x00004000     cursor
     4n     LISTofVALUE                    value-list

  VALUEs
     4     PIXMAP                          background-pixmap
          0     None
          1     ParentRelative
     4     CARD32                          background-pixel
     4     PIXMAP                          border-pixmap
          0     CopyFromParent
     4     CARD32                          border-pixel
     1     BITGRAVITY                      bit-gravity
     1     WINGRAVITY                      win-gravity
     1                                     backing-store
          0     NotUseful
          1     WhenMapped
          2     Always
     4     CARD32                          backing-planes
     4     CARD32                          backing-pixel
     1     BOOL                            override-redirect
     1     BOOL                            save-under
     4     SETofEVENT                      event-mask
     4     SETofDEVICEEVENT                do-not-propagate-mask
     4     COLORMAP                        colormap
          0     CopyFromParent
     4     CURSOR                          cursor
          0     None

Немножко сложнее, но надеюсь более-менее понятно… Сложность здесь из-за того, что в запросе можно передать кучу параметров окна разного вида и формата. Но по сути все идет последовательно и более-менее логично.


После получения этого запроса, сервер создает окно с заданными параметрами. Но это окно не появится, так как все еще не показано на экране. Делаем это через запрос «MapWindow». На фоне прежнего, он совсем простенький:


Смещение Длина Имя поля Значение Заметки
0 1 opcode 0x08 Операция MapWindow == 8
1 1 0x00 Не используется
2 2 length 0x0002 Длина 8 байт.
4 4 wid 0x12345678 Это ID нашего окна.

На сокет уходит: 08 00 02 00 78 56 34 12 а окно становится видным.


Ответы


Сервер тоже присылает нам по сокету информацию. Она бывает 3 вида: Ответы (Reply), События (Events) и Ошибки (Errors).


Все три вида имеют длину минимум 32 байта. (А события и ошибки всегда точно 32 байта). Так что чтение из сервера происходит всегда порциями в 32 байта и если это Reply из тела ответа берем длину дополнительной части и читаем ее тоже.


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


  1. Ответы на запросы (Reply). Если запрос предполагает ответ от сервера, то сервер его присылает по сокету, как только обработает запрос. Если ответ содержит информацию, которая помещается в 32 байта, то это все что нужно принять. Если ответ длиннее, то в его теле содержится длина дополнительной части ответа.

Общий формат ответа такой:


Смещение Длина Имя поля Значение Заметки
0 1 code 1 1 == Reply
1 1 ? ? Или не используется или используется для какой-то часть ответа, длиной в 1 байт.
2 2 sequence s Это номер запроса, на котором ответ.
4 4 length n Длина ответа сверх первых 32 байта в двойных словах. Если не 0, то надо прочитать с сокета еще 4*n байта, чтобы взять весь ответ.

  1. События (Events). Содержат те же 32 байта и генерируются в ответ на какие-то события в GUI. Чтобы получать некоторые события, клиент должен подписаться на них, когда создает окно, например.

Некоторые события общесистемного характера присылаются всегда и всем.


Формат событии такой:


Смещение Длина Имя поля Значение Заметки
0 1 code 2..127 [+128] > 1 для событий. Если событие прислано от другого клиента через SendEvent, то к номеру события прибавляется 128. (Старший бит устанавливается в 1).
1 1 detail ? Деталь о событии если помещается в 1 байт.
2 2 sequence ? Номер запроса, после которого случилось событие.
4 4 timestamp time Время возникновения события
8 24 ? Зависят от события.

  1. Ошибки. Присылаются если какой-то запрос клиента нельзя было исполнить потому что содержит какую-то ошибку в данных или параметрах. Формат ошибки такой:

Смещение Длина Имя поля Значение Заметки
0 1 code 0 0 == Error
1 1 error code 1..255 Код ошибки.
2 2 sequence ? Номер ошибочного запроса.
4 28 data ? Подробности об ошибке. Зависит от кода ошибки.

Подключение.


А сейчас сделаем шаг назад и рассмотрим наверное самое сложное в X11 – подключение к серверу. К сожалению процедура сложная и запутанная и является камнем преткновения для прямого использования X11.


Именно подключение поднимает уровень вхождения в технологию.


Как мы увидели само использование протокола достаточно просто. Но подключение – это что-то с чем-то!


Само подключение по сути простое – создаем сокет и выполняем connect на него. Но сперва надо узнать адрес сервера. Для этого есть алгоритм:


Смотрим на содержание переменной окружения DISPLAY. Если существует, она содержит адрес X11 сервера в формате: [host]:D.S.


host – это хост сервера. Это может быть имя домейна, может быть строкой "/unix" или просто отсутствовать. Отсутствующий host равен "/unix" и означает что сервер слушает на unix domain сокете на локальной машине.


Кстати, это самый частый случай. Если host присутствует, это значит что подключаться надо к этому хосту, по TCP, через IP6 адрес.


D это номер дисплея, а S это номер экрана. В большинстве случаев на современных конфигурациях номер экрана будет 0, даже если мониторов больше одного. Все они виртуально объединены в один экран.


От номера дисплея зависит порт подключения к серверу. Если по TCP, то сервер слушает на порт 6000+D. Если подключаемся через unix domain сокет, он находится по адресу /tmp/.X11-unix/X{D} – то есть, нулевой дисплей на /tmp/.X11-unix/X0, первый на /tmp/.X11-unix/X1 и т.д.


И вот, мы подключились к сокету. После подключения, нельзя просто так посылать запросы. Надо сперва отправить на сервер информацию о себе и авторизоваться на сервере.


Все это содержится в первом (а точнее нулевом) запросе, который нестандартный и содержит:


Смещение Длина Имя поля Значение Заметки
0 1 byte_order "B" или "l" B (0x42) означает BIG-ENDIAN, a "l"(0x6c) – little-endian.
1 1 0x00 Не используется
2 2 major_ver 11 Мажорная версия протокола
4 2 minor_ver 0 Минорная версия протокола
6 2 auth_proto_len n Длина имя протокола авторизации
8 2 auth_data_len d Длина данных авторизации
10 2 not_used ? Выравнивание
12 n auth_proto string Протокол авторизации
12+n pad(n) Выравнивание к двойному слову.
12+n+pad(n) d auth_data string Данные авторизации
12+n+pad(n)+d pad(d) Выравнивание к двойному слову.

Первый байт определяет в каком формате наша программа понимает числа. Сервер будет присылать нам все числа длиннее одного байта в этом формате и будет понимать числа которые мы присылаем в этом формате.


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


Потом следует имя протокола авторизации и собственно данные авторизации. Это типа доказательство, что эта программа имеет право подключаться к серверу X11.


Откуда берем имя протокола и данные об авторизации? Они находятся в файле, путь к которому находится в переменной окружения $XAUTHORITY. Если эта переменная не существует можно поискать в файле $HOME/.Xauthority – это самый распространенный вариант. Если у вашего приложения нет прав доступа к этому файлу или файл не существует, то значит у вас нет доступа к этому X11 серверу.


Файл бинарный и его формат не слишком хорошо задокументирован. Мне пришлось спрашивать на stackoverflow чтобы разобраться, да и то получилось лишь частично.


Так, структура файла, это последовательность записей вот таких структур:


typedef struct xauth {
    unsigned short   family;
    unsigned short   address_length;
    char            *address;
    unsigned short   number_length;
    char            *number;
    unsigned short   name_length;
    char            *name;
    unsigned short   data_length;
    char        *data;
} Xauth;

Но во первых, в файле, конечно указателей нет. Все строки вписаны просто последовательно, символ за символом в файле. Во вторых – все двухбайтовые числа всегда являются big-endian. Вне зависимости от архитектуры компьютера.


address – это HOST адрес сервера.


number – это номер дисплея, который мы уже определили из переменной $DISPLAY, записанный в виде текстовой строки!


name – это имя протокола. В настоящем времени и насколько я знаю, используется только MIT-MAGIC-COOKIE-1 протокол.


data – это массив байтов, примерно вот такой: 07 bd 70 26 1а ab 4c 7c 35 3c c1 b2 cc 25 a2 29. который мы должны переслать серверу в знак, что у нас доступ позволен.


Перебираем этот файл пока не найдем запись, у которой HOST совпадает с хостом из $DISPLAY и номер дисплея с номером дисплея из $DISPLAY. Из этой записи достаем имя протокола и данные авторизации.


И так мы собрали все необходимые данные о нулевом запросе и формируем его:


Смещение Длина Имя поля Байты Пояснение
0 1 byte_order 0x6c l
1 1 0x00
2 2 major_ver 0x0b 0x00 0x000b
4 2 minor_ver 0x00 0x00 0x0000
6 2 auth_proto_len 0x12 length("MIT-MAGIC-COOKIE-1") = 18
8 2 auth_data_len 0x10 length(cookie) = 16
10 2 0x00 0x00 не используется. Просто выравнивает.
12 18 auth_proto 4d 49 54 2d 4d 41 47 49 43 2d 43 4f 4f 4b 49 45 2d 31 "MIT-MAGIC-COOKIE-1"
30 2 pad(0x12) 0x00 0x00 Выравниваем до 20.
32 16 auth_data 07 bd 70 26 1а ab 4c 7c 35 3c c1 b2 cc 25 a2 29

К серверу уходит: 6c 00 0b 00 00 00 12 00 10 00 00 00 4d 49 54 2d 4d 41 47 49 43 2d 43 4f 4f 4b 49 45 2d 31 00 00 07 bd 70 26 1а ab 4c 7c 35 3c c1 b2 cc 25 a2 29 – всего 48 байтов.


На что сервер может ответить тремя возможными ответами. Вариант ответа определяется по первому байту. Он может быть:


0: Подключение отклонено. Весь ответ содержит:


Смещение Длина Имя поля Байты Пояснение
0 1 reply 0x00 Failed
1 1 n n Длина текстового ответа.
2 2 major_ver 0x0b 0x00 Мажорная версия протокола.
4 2 minor_ver 0x00 0x00 Минорная версия протокола.
6 2 data_len (n+p)/4 Длина дополнительной информации в двойных словах.
8 n data Какое-то текстовое сообщение об ошибке.
8+n p pad(n) Выравнивание до двойного слова.

2: Нужна дополнительная аутентификация. Я этого варианта не изучал потому что так и не успел найти систему, которая так бы отвечала…


1: Подключение принято.


Самый хороший для нас вариант. Ответ очень длинный и сложный, содержит главные параметры системы, которые мы должны запомнить и использовать позже в наших запросах.


Я так и не смог нарисовать такую сложную табличку, чтобы все разложить по полочкам. Поэтому вот вам описания ответа из документации протокола:


     1     1                               Success
     1                                     unused
     2     CARD16                          protocol-major-version
     2     CARD16                          protocol-minor-version
     2     8+2n+(v+p+m)/4                  length in 4-byte units of
                                           "additional data"
     4     CARD32                          release-number
     4     CARD32                          resource-id-base
     4     CARD32                          resource-id-mask
     4     CARD32                          motion-buffer-size
     2     v                               length of vendor
     2     CARD16                          maximum-request-length
     1     CARD8                           number of SCREENs in roots
     1     n                               number for FORMATs in
                                           pixmap-formats
     1                                     image-byte-order
          0     LSBFirst
          1     MSBFirst
     1                                     bitmap-format-bit-order
          0     LeastSignificant
          1     MostSignificant
     1     CARD8                           bitmap-format-scanline-unit
     1     CARD8                           bitmap-format-scanline-pad
     1     KEYCODE                         min-keycode
     1     KEYCODE                         max-keycode
     4                                     unused
     v     STRING8                         vendor
     p                                     unused, p=pad(v)
     8n     LISTofFORMAT                   pixmap-formats
     m     LISTofSCREEN                    roots (m is always a multiple of 4)

FORMAT
     1     CARD8                           depth
     1     CARD8                           bits-per-pixel
     1     CARD8                           scanline-pad
     5                                     unused

SCREEN
     4     WINDOW                          root
     4     COLORMAP                        default-colormap
     4     CARD32                          white-pixel
     4     CARD32                          black-pixel
     4     SETofEVENT                      current-input-masks
     2     CARD16                          width-in-pixels
     2     CARD16                          height-in-pixels
     2     CARD16                          width-in-millimeters
     2     CARD16                          height-in-millimeters
     2     CARD16                          min-installed-maps
     2     CARD16                          max-installed-maps
     4     VISUALID                        root-visual
     1                                     backing-stores
          0     Never
          1     WhenMapped
          2     Always
     1     BOOL                            save-unders
     1     CARD8                           root-depth
     1     CARD8                           number of DEPTHs in allowed-depths
     n     LISTofDEPTH                     allowed-depths (n is always a
                                           multiple of 4)

DEPTH
     1     CARD8                           depth
     1                                     unused
     2     n                               number of VISUALTYPES in visuals
     4                                     unused
     24n     LISTofVISUALTYPE              visuals

VISUALTYPE
     4     VISUALID                        visual-id
     1                                     class
          0     StaticGray
          1     GrayScale
          2     StaticColor
          3     PseudoColor
          4     TrueColor
          5     DirectColor
     1     CARD8                           bits-per-rgb-value
     2     CARD16                          colormap-entries
     4     CARD32                          red-mask
     4     CARD32                          green-mask
     4     CARD32                          blue-mask
     4                                     unused

Но как бы и сложным это не выглядело бы, всю информацию не надо запоминать или даже анализировать.


Мы от этого ответа возьмем только то, что важно для нас. И это во первых два числа из полей: resource-id-base и resource-id-mask. Они дают нам диапазон в котором надо генерировать ID константы для всех объектов GUI. (Не забывайте, что в X11 все идентификаторы объектов генерируются на стороне клиента, а серверу именно клиент говорит какой будет ID окна или других объектов.)


Так, у сервера есть только одно ограничение – каждой программе он выделяет диапазон в котором идентификаторы должны помещаться. Так идентификатор должен содержать только те биты которые в resource-id-mask установлены в единицу. И идентификатор должен начинать с resource-id_base.


Еще надо запомнить для будущего использования диапазон клавиатурных кодов (min-keycode/max-keycode), найти в ответе те форматы изображений, которые программа может использовать и которые ей удобны.


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


Все остальное более или менее можно проигнорировать.


Я обычно ищу во всем этом многообразии тот SCREEN, который меня устраивает (32 бит TrueColor) и использую только его. А если сервер такое не поддерживает, просто заканчиваю работу. Это сильно упрощает работу и код.


Заключение


Ну это все для первого раза. Надеюсь сумел все объяснить яснее чем в документации и дать то понимание, которое позволит дальше свободно читать документацию (А она и правда хороша, если человек умеет ее понимать).


В качестве упражнения предлагаю конкурс-челендж: Написать программу на bash которая устанавливает соединение с X сервером и создает и показывает окно с заголовком «X11 rules».


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


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

Комментарии (140)


  1. kmeaw
    06.01.2024 17:46
    +5

    #!/bin/bash
    
    display=${DISPLAY%.*}
    
    byte_order='6c'
    noop='00'
    major_ver='0b00'
    minor_ver='0000'
    auth_proto_len='1200'
    auth_data_len='1000'
    auth_proto=$(echo -n 'MIT-MAGIC-COOKIE-1' | xxd -p)
    auth_data="$(xauth list | grep "$(hostname)/unix$display " | awk '{print $3}')"
    
    message="${byte_order}${noop}${major_ver}${minor_ver}${auth_proto_len}${auth_data_len}0000${auth_proto}0000${auth_data}"
    
    u=$(mktemp -u)
    v=$(mktemp -u)
    mkfifo $u
    mkfifo $v
    
    nc -U /tmp/.X11-unix/X0 <$u >$v & pid=$!
    trap "rm -f $u $v; kill $pid" EXIT
    
    xxd -r -p <<<$message >$u
    hexdump -C $v


  1. igor_suhorukov
    06.01.2024 17:46
    +11

    Спасибо что потратили время!

    понял и сам попробую напрограммировать.

    Просто почитал с удовольствием, а ассемблер оставил в 2003 - во времена студенчества, вместе с другими теплыми воспоминаниями об учебе. К сожалению, работодатели эти знания не оплачивают - всем нужен результат быстрее и даже GC в большинстве проекто в не помеха.


    1. johnfound Автор
      06.01.2024 17:46
      +6

      Так я же не ассемблер предлагаю в качестве упражнения, а bash. Он полезнее будет в качестве повышения квалификации.


      1. igor_suhorukov
        06.01.2024 17:46
        +9

        bash. Он полезнее будет в качестве повышения квалификации.

        Мне надолго хватило воспоминаний от написания и поддержки bash скриптов в швейцарском инвестбанке - стирал их потом скотчем, который не в рулонах ????‍????. Дам другим программистам отличиться и понаблюдаю в стороне!


      1. CrashLogger
        06.01.2024 17:46
        +2

        Ну вот как раз на ассемблере оно имеет какой-то смысл. Заслать на удаленную машину shell-код с GUI управлением ))


      1. vassabi
        06.01.2024 17:46
        -2

        можно еще питон вместо баша.
        Заодно - чтобы запускать из винды, а показывать окошки в линуксе ;)


  1. MinimumLaw
    06.01.2024 17:46
    +7

    Спасибо. Продолжать определенно стоит. Вот только... Чтение вслух и с выражением X Window System Protocol или его аналогов... Ну не совсем то, что ожидается.

    Скорее интересны некоторые не обозначенные в документации (подразумеваемые) детали. В частности подключаться можно не только через сокеты. Есть соединения через последовательный порт. И вообще через абсолютно любой канал связи. Именно в этом и прелесть X11 - он тунелируется куда угодно.


  1. Coast
    06.01.2024 17:46
    +13

    Для коллекции Хабра материал весьма ценный. Никогда с Иксами не приходилось на столько низком уровне работать (только через XLib), но в случае если вдруг потребуется - статья есть. Часто находил по другим технологиям ответы на свои вопросы, когда приходилось копать до самых глубин.


  1. Fedarod
    06.01.2024 17:46
    +1

    Вот не первый раз с таким сталкиваюсь, что только фанаты объявят 2024 годом Wayland'а, так вот вам статья по Х11. Не очень красиво выглядит, как будто автор устраняет конкурентов, вот вам задачка на зарыться поглубже, пока я сниму сливки с рынка... Может, конечно, совпадение, но сравнение было бы более чем уместно в подобной статье.


    1. alexs963
      06.01.2024 17:46
      +2

      Сколько лет там пилят этот вейланд, а он всё ещё не готов для нормального использования.


      1. Cfyz
        06.01.2024 17:46
        +9

        И тем не менее, основные дистрибутивы уже включают Wayland по умолчанию (плюс конечно XWayland на фоне).

        С Wayland смешно уже конечно, ему 15 лет уже, ещё через 6 ему будет столько же, сколько было X11 когда начали разрабатывать Wayland, можно будет новый проект запускать. Мне самому кое-какие моменты в разработке Wayland не нравятся.

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


        1. Gorthauer87
          06.01.2024 17:46
          +2

          Ну я первый раз Wayland сессию в гноме щупал году ещё в 14ом и она работала, а более менее перешёл на него году, наверное, в 17ом.

          Но вот окончательно необходимость в нем появилась, когда купил 4К монитор, на нем дробное масштабирование работает нормально лишь в вейланде. Тут уже никуда не денешься. Да и вот в Asahi Linux даже не стали пытаться делать дрова для иксов, там только Вейланд. По сути его больше всех задержала нвидия, на других видюхах он уже давно работает стабильно.

          А на момент старта разработки вейланда иксам было уже 25 лет почти. Их же в 84 году начали делать, то была совсем другая эпоха.


        1. funny_falcon
          06.01.2024 17:46
          +2

          В Ubuntu 23.10 что-то сломали в поддержке GPU, встроенного в Ryzen. В итоге сессия Wayland на ноуте стала очень нестабильной, и вешает комп в течении часа-двух. Пришлось вернуться на X11. На X11 тоже есть определенные проблемы, но хотя бы не вешается.

          Гугление показало, что проблема масштабная, и кажется цепляет не только Ubuntu.


          1. kt97679
            06.01.2024 17:46
            +1

            У меня 23.10 и Ryzen. Я перешел на ядро 6.6.8 из mainline ppa. После этого краши прекратились.


            1. funny_falcon
              06.01.2024 17:46
              +1

              Спасибо, попробую.


              1. funny_falcon
                06.01.2024 17:46
                +2

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

                Ещё раз спасибо!


    1. johnfound Автор
      06.01.2024 17:46
      +10

      Ну, виноват, я немножко торможу и только сейчас разобрался. Хотя мне кажется, что еще много, много лет мы будем читать статьи насчет X11 vs Wayland. Да и с самом X11 ничего не случится будет Xwayland и только.


      1. Cfyz
        06.01.2024 17:46
        +1

        С X11 скорее всего случится то, что приложения просто лавинообразно перестанут его поддерживать.

        Большинство и так написаны с использованием основных фреймворков типа Qt или GTK, они даже не заметят что что-то произошло. Другие разработчики будут вынуждены переписать/обновить, потому что приложения при работе через X начнут выглядеть плохо (дробное масштабирование нынче все более распространено), не будут поддерживать всякие модные жесты, не смогут взаимодействовать с остальными приложениями в системе, появятся проблемы с задействованием новых API аппаратного ускорения и прочие мелочи, которые будут продолжать копиться.


    1. CrazyOpossum
      06.01.2024 17:46

      В смысле автор получает отчисления с продаж иксов? Wayland недружелюбен к разработчикам, Поттеринг недружелюбен ни к кому, в Арче вэйланд пока ещё не по дефолту, и Mate (gnome 2), кажется, тоже с ним не до конца дружит.


      1. vtb_k
        06.01.2024 17:46
        +2

        Wayland недружелюбен к разработчикам,

        Что это за бред? Доказательства будут?

        Поттеринг недружелюбен ни к кому

        Этот чувак уже вообще в некрософт работает

        в Арче вэйланд пока ещё не по дефолту

        в арче консоль по дефолту, иксы или вейланд ставят по желанию.

        Mate (gnome 2), кажется, тоже с ним не до конца дружит.

        Mate уже давно на gtk 3.

        Не представляю, за что вам плюс поставили с таким уровнем экспертизы


  1. FSA
    06.01.2024 17:46
    +1

    Буквально недавно вышел ролик про историю X11 - https://www.youtube.com/watch?v=k8PaxLYOYdo
    Не вдавался в подробности, но ролик заставляет формировать отношение, что X11 устарел и создаёт одни проблемы. Интересно было бы узнать правда ли рассказана в ролике и, если нет, то можно даже статью на Хабр написать.


    1. bogolt
      06.01.2024 17:46
      +10

      Ткнул в случайное место ролика. Услышал что "Х11 и Вэйленд являются протоколами, то есть файлами в формате xml". Не стал слушать дальше.


      1. MountainGoat
        06.01.2024 17:46
        +4

        "Что такое протокол? X11 и Wayland являются так называемыми протоколами, то есть документами в формате XML, в которых описаны все необходимые интерфейсы. На основе этих данных создаются стандартные библиотеки, которые разработчик уже использует для написания дисплейного сервера."


        1. bogolt
          06.01.2024 17:46
          +5

          Тем не мене протокол не является документом. Даже если в документе содержится описание этого протокола.


          1. ef_end_y
            06.01.2024 17:46
            +3

            Класс. Создатель ролика не совсем корректно выразился, а ролик зафейлили за то, что кто-то попал в это случайное место. Мне было бы обидно, что кто-то так рецензию пишет на мои видосы. Вот так рандом сыграл злую шутку


            1. nick758
              06.01.2024 17:46
              -1

              Ну если учесть, что когда появился X11, ещё никакого XML в проекте не было, то доверие к такому ролику сразу пропадает.


              1. alef13
                06.01.2024 17:46
                +1

                Xml не было, был sgml. Тем не менее, ляп в ролике это не оправдывает :)


            1. Mingun
              06.01.2024 17:46
              +1

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


  1. wert_lex
    06.01.2024 17:46
    +11

    Статья - огонь.
    Есть вопрос: идея архитектуры иксов в общих чертах понятна. А чем принципиально Wayland со товарищи отличается?


    1. saege5b
      06.01.2024 17:46
      +14

      Это как комикс про один универсальный протокол.

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

      В какой-то момент кому-то показалось что он сможет лучше.


    1. lrrr11
      06.01.2024 17:46
      +1

      принципиально wayland - все тот же сокет, к которому мы подключаемся и гоняем сообщения туда-сюда. Тем не менее,

      • ответы сервера не делятся на типы. Что написано в протоколе, то он и посылает, причем строго асинхронно

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

      • нет никакого XAuthority. Однако в Wayland мы не имеем доступа к окнам других клиентов, и вообще ничего о них не знаем

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


  1. pa77
    06.01.2024 17:46

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


    1. johnfound Автор
      06.01.2024 17:46
      +3

      Хм, здесь сервер находится у потребителя, а приложение может быть где угодно. Чтобы подменить сервер, надо залезть в компьютер потребителя и как-то заменить .Xauthority - а у него права доступа 600о.


      1. pa77
        06.01.2024 17:46
        +1

        Были случаи, когда сервер получал экран от другого клиента. Теперь буду знать, на что посмотреть.

        Спасибо! Знание, как все устроено - бесценно!


      1. kmeaw
        06.01.2024 17:46

        Но ведь клиент X11 не аутентифицирует сервер - если подменный сервер, например, запустить с флагом "-ac", то клиент без проблем подключится.


        1. Mingun
          06.01.2024 17:46

          А зачем клиенту это делать? Чтобы не разрешать подключившимуся к нему пользователю показывать картинку? А зачем, если он и так уже подключился к машине, где приложение-графический-клиент запущено? Чтобы ему на ровном месте жизнь усложнить?


          1. Wesha
            06.01.2024 17:46

            А зачем, если он и так уже подключился к машине, где приложение-графический-клиент запущено?

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


            1. Mingun
              06.01.2024 17:46

              Гм. И что? От того, что Вася и Петя подключаются не один после другого, а одновременно нужно контролировать, а можно ли им это делать или что? И повторюсь, еще раз, зачем это делать, если они и так уже подключились к машине и все что угодно могут с ней делать? Думаете, отсутствие окошек и мышки при этом кого-то остановит?


          1. kmeaw
            06.01.2024 17:46

            Клиент может это сделать в результате ошибки пользователя (опечатка или не тот конфиг используется) или целенаправленной атаки злоумышленника (DNS или ARP poisoning).

            Если пофантазировать, то можно представить себе такой сценарий - в секретном бункере состоялась встреча шпионов, и ведущий встречи достал свой ноутбук (с правильным ~/.Xauthority), и ввёл команду: DISPLAY=bigscreen.spy.corp:0 xmessage $(cat meeting.notes), чтобы его соседи увидели файл на большом экране в той же комнате. А контрразведка соседней страны знает, что так начинается каждая встреча, и хочет этот meeting.notes прочитать.

            SSH же, например, пытается не дать пользователю подключиться к "не тому" хосту даже в том случае, когда для аутентификации клиента на сервере используется ключ. При этом никто не задаёт вопрос "А зачем клиенту это делать?".


            1. qw1
              06.01.2024 17:46

              В таком случае, надо запретить HDMI и DVI, а все мониторы снабдить SSH-ключами. А то вдруг в путанице проводов шпиёны вставят mitm между хостом bigscreen.spy.corp и экраном. Вы бы хотели, чтобы все мониторы подключались к видекартам исключительно по SSH-ключам?


              1. kmeaw
                06.01.2024 17:46

                И так появился HDCP. :)

                Вставить аппаратный mitm в провод или случайно воткнуть ноутбук в монитор злоумышленника часто сложнее, чем провести атаку на что-то из ARP, IP, DNS или опечататься при вводе DISPLAY=.

                Но постепенно изобретаются способы аутентифицировать оборудование. На предприятиях решаются проблемы BadUSB, неавторизованные флешки блокируются. Док-станции с Thunderbolt требуют явного разрешения от пользователя для работы.


                1. qw1
                  06.01.2024 17:46

                  И так появился HDCP. :)

                  Нет. HDCP не решает задачу аутентификации монитора )) Потому что ключами не управляет пользователь, ему подсунь любой другой HDCP-монитор, он ничего не заметит.


    1. alex-open-plc
      06.01.2024 17:46
      -1

      Работать через ssh религия не позволяет?


  1. NeoCode
    06.01.2024 17:46
    +4

    На практике вряд ли буду с ним пересекаться (есть же Qt), но тема интересная, и вообще в таких достаточно низкоуровневых статьях есть что-то теплое и ламповое. Лаконичность и совершенство оценить не могу, нужен опыт, но ведь кроме достоинств есть и недостатки? Зачем тогда пилят Wayland на замену?


    1. feelamee
      06.01.2024 17:46
      +2

      насколько я понял, автор статьи рассказывает о протоколе. А всем не нравится собственно его имплементации. Хотя я не разговаривал с кем-то, кто серьезно в этом разбирается.
      Мне в голову приходит идея просто написать новую имплементацию протокола, если проблема не в нем.


      1. Cfyz
        06.01.2024 17:46
        +1

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


      1. johnfound Автор
        06.01.2024 17:46
        +4

        Все немножко посложнее. Протокол по сути задуман прекрасно и реализован тоже прекрасно. Но в стандартной имплементации есть такая куча легаси, что аж дух захватывает. Вот например графические возможности. По идеи, всё рисование и вывод текста должно происходит на сервере, через всякие графические ускорители. И все это работает. Только там нет векторные шрифты, нет сглаживания линий. Много чего нет. Все это потом добавили в виде расширений, но там почти нет документации. С другой стороны, X все еще поддерживает все форматы графики – например монохромные режимы 1bpp или цветные 4bpp. Которые практически никому не нужны.

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

        Я бы оставил только 32bpp, интегрировал бы libFreeType вместе с libFontConfig. Переписал бы все основные запросы чтобы работали по современному с всякими antialias и сабпиксельными сглаживаниями. Если все сделать по уму, мне кажется даже совместимость с очень старым софтом можно обеспечить.

        Но люди выбрали сделать все с нуля. Может это и оправдано. Но в итоге будут просто Wayland и XWayland.


    1. Cfyz
      06.01.2024 17:46
      +4

      Зачем нужен Wayland это тема для отдельной огромной статьи. Возможно, проще будет погуглить и по диагонали пролистать тезисы.

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

      За тридцать с лишним лет приоритеты и практики разработки поменялись. Прокидывать интерфейс по сети никому не нужно (не стоит путать с удаленным рабочим столом), при декодировании видео на YouTube нежелательно даже лишнее копирование (потому что это дополнительные милливатты драгоценной батареи ноутбука или телефона), а воровать пароли и вовсе некрасиво.

      X11 латали костылями изо всех сил, но в какой-то момент надоело.


      1. johnfound Автор
        06.01.2024 17:46
        +5

        (не стоит путать с удаленным рабочим столом)

        Если под удаленным столом понимаете перекачивание видео по сети в плохом качестве, то я двумя руками за X11.


        1. Cfyz
          06.01.2024 17:46
          +4

          Ни один из распространенных на практике протоколов удаленного доступа к рабочему столу не использует модель X. И тем не менее, все прекрасно работает без "перекачивания видео по сети в плохом качестве" =).

          Вариант с передачей по сети команд по работе с фиксированным набором относительно высокоуровневых примитивов пользовательского интерфейса (окна, контролы, текст и т. п.) просто не работает в общем виде. Современный десктоп намного сложнее в графическом плане.

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


          1. LF69ssop
            06.01.2024 17:46
            +6

            И тем не менее, все прекрасно работает без "перекачивания видео по сети в плохом качестве"

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


            1. hMartin
              06.01.2024 17:46
              -3

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


              1. vkni
                06.01.2024 17:46
                +4

                У вас нет мобильного телефона, который умеет раздавать сеть? И вы никогда не ездите на дачу?


            1. Cfyz
              06.01.2024 17:46
              -1

              И какое это имеет отношение к вышесказанному?

              Во-первых, видео а-ля стриминг игр нигде и не перекачивается, ни в плохом качестве, ни в каком.

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

              X просто не подходит, даже если он по тоненькому каналу может работать. Если "не передавать контент вне оконных примитивов", ограничившись только чистым X, то это будет работать только с X, то есть в довольно ограниченном виде. Если вам хватает простецких окон терминала и блокнота с древними шрифтами -- прекрасно, остальному миру не хватило. Если же прокинуть через X десктоп с композицией, а в нем ещё и современный браузер, то там уже по сути VNC начинает ходить, то есть все те же копии картинок.


              1. LF69ssop
                06.01.2024 17:46
                +5

                И какое это имеет отношение к вышесказанному?

                Самое непосредственное.

                Все эти vpn не работают на не скоростных каналах. А Х работает, не дает художества сравнимого качества, а дает функционал необходимый для нормальной работы.

                ограничившись только чистым X, то это будет работать только с X, то есть в довольно ограниченном виде

                Ровно то, для чего он и создан.

                Просто в данном случае разменяли функционал на "свистелки и перделки". Право выбора каждого конечно. Если у вас прекрасные каналы связи то зачем вам аскетизм? А если связь не сильно прекрасная то вам не до красот.

                Сейчас, кстати, на 128К не то что vpn, не каждый сайт нормально откроется. У половины по таймауту что-то да отвалится без чего многое прочее не прогрузится.


                1. veryboringman
                  06.01.2024 17:46
                  +2

                  Вообще-то X очень плохо работает на низкоскоростных каналах, Вы, вероятно, не мучались с этим в 90е.

                  Он был задуман для работы в локальной сети, на X-терминалах. Т.е. от 10 мбит/c минимум.


                1. Cfyz
                  06.01.2024 17:46
                  +2

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

                  Добрая часть UI уже давно не будет работать по сети в том виде, в котором это задумывалось в X изначально, часть вещей в принципе не может работать по сети и явно или косвенно опирается на предположение, что все локально. А худо-бедно прокидывается оно потому, что вместо высокоуровневых примитивов там передаются целые области изображения -- как в VNC (а не VPN, надеюсь вы просто опечатались), только криво.

                  В итоге "как задумано" оно работает только в примитивных сценариях, которые нельзя назвать общим решением. Либо оно работает так же, как и остальные протоколы, только ещё и X там ни к селу, ни к городу.


              1. vikarti
                06.01.2024 17:46

                Во-первых, видео а-ля стриминг игр нигде и не перекачивается, ни в плохом качестве, ни в каком.

                тот же Parsec - в том числе специально для стриминга игр и создан.

                И прилично в таком режиме работает.


                1. Cfyz
                  06.01.2024 17:46

                  Соглашусь, утверждение было сформулировано слишком категорично.

                  Однако я имел в виду обычные, популярные протоколы доступа к удалённому рабочему столу, где не предполагается много видео. В таком случае старый добрый подход с как бы, типа, если один глаз закрыть, lossless передачей пожатых прямоугольников с изменившимися пикселями работает на удивление хорошо.

                  Хотя с современными кодеками, которые сами могут разбить кадр на произвольные области с индивидуальными параметрами сжатия, граница становится довольно размытой. Не удивлюсь, если к AV2 или H.266 проще и эффективнее будет тупо кодеком весь экран пожать с указанным битрейтом и не париться.


          1. vikarti
            06.01.2024 17:46

            И вот при этом тот же RDP (клиент - win(с клиентом на маке пусть и все равно MS RDP уже не так хорошо но терпимо, с клиентом на Linux с KRDC/Remmina или на андроид - мыло на тексте) и сервер тоже win) может передавать рабочий стол win без мыла как минимум в некоторых случаях и на пинг в 200 мс по сотовой связи с 5 mbit/s - ему плевать, правда видео с youtube - играет не очень хорошо даже если от клиента до сервера 5 метров и гигабитный ethernet.

            А вот например Parsec - чувствителен к полосе, чувствителен к задержкам, качество приличное везде но мылит текст даже в ситуации когда между машинами 5 метров и тот же самый гигабитный ethernet. И для нормальный работы надо аппаратные кодеры h.264 а лучше и h.265


        1. vvzvlad
          06.01.2024 17:46
          -8

          Если под удаленным столом понимаете перекачивание видео по сети в плохом качестве, то я двумя руками за X11.

          И работать оно будет только между двумя одинаковыми дистрибутивами линукса, не сильно разошедшимися в мажорных версиях? Нет, спасибо, я хочу универсальный рабочий стол.


          1. johnfound Автор
            06.01.2024 17:46
            +8

            В смысле? Причем здесь версии Линукса?


          1. mpa4b
            06.01.2024 17:46
            +9

            Враки какие-то. У меня прокидывание иксов через ssh работает примерно всех со всеми, даже с netbsd, не говоря уж про любые версии линукса на любых платформах, включая всяческие SBC на армах. А вот вялый эту офигенную фишку решил выкинуть.


          1. vkni
            06.01.2024 17:46
            +6

            Оно работает даже между OpenVMS и Windows/OSX — я проверял. Рутинно использовал приложение на RedHat Linux и десктоп под Windows c Xming.

            Вообще, этих Х-серверов понаделали под все платформы, наверняка и под Haiku есть, и под Колибри можно заделать.


          1. mizabrik
            06.01.2024 17:46
            +1

            Открывал на сервере в MacOS окно Emacs из докера с guix — всё отлично работало!

            Картинку про троллейбус из буханки хлеба можно не присылать :)


            1. vvzvlad
              06.01.2024 17:46

              Ладно-ладно, я был неправ, оно настолько легаси, что работает везде одинаково! :)


              1. vkni
                06.01.2024 17:46
                +4

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


        1. a-n-d
          06.01.2024 17:46
          +2

          DRM позволяет рендерить прямо в память. Тот же user-space OpenGL на это полагается. Иксы работают как враппер оконного окружения для этих целей и высокодинамичный контент также будут копировать "в хвост и в гриву". Всевозможные on-memory видео-захват, overlay-контент и offscreen-рендеринг оконными примитивами иксов никак не покрываются.

          Так что не оконным окружением единым, как говорится. В нем нет волшебных фич для решения этих задач. @Cfyz абсолютно прав: одно дело передавать оконные примитивы, другое - контент внутри окон.


          1. LF69ssop
            06.01.2024 17:46

            Так может не передавать контент вне "оконных примитивов"? Может инструментом стоит выполнять те задачи на которые он рассчитан, а не те которые вам нужно?


          1. aegor
            06.01.2024 17:46
            +6

            Полностью согласен. Только не DRM, а DRI. С момента появления первых композитных менеджеров окон эпоха протокола X11 прошла насовсем. В какой-то мере программы ещё продолжали общаться с Xlib, но уж точно не с X11 protocol. А это было очень давно. Это относилось даже к шрифтам. И с тех пор именно API DRI определял вектор развития оконных интерфейсов *nix. А поскольку стандарта DRI не существовало, тав вендоры работали с extensions, кто во что горазд. Вот и появилась идея создания низкоуровневого композитного менеджера, такого, как Wayland, а уж над ним конструкции низкоуровневого графического API, Vulcan. Кстати, именно после появления концепции связки Wayland/Vulkan, Apple задумалась о создании своего собственного низкоуровневого API (Metal). Apple, кстати, даже участвовала в консорциуме Vulcan, потом отвалилась от него.

            Ни DRI, ни Wayland, ни Vulcan не имеют сетевого протокола как такового, потому что это физически нереализуемо с разумным уровнем полезности.

            Так что темы, которая затрагивает эта статья прокисли году этак в 2006-м. Кстати, автор не отметил следующих особенностей/недостатков протокола:

            1. Работа со шрифтами. Для нормальной работы шрифты на клиенте и сервере должны в точности совпадать. В протоколе не было механизма ни загрузки шрифтов, ни их предварительной передачи/кэширования.

            2. Абсолютно перегруженный нелепыми понятиями и уровнями keyboard/mouse mapping, который никогда по-человечески не работал. Для этого они были вынуждены делать даже костыли через Dbus с пробросом сигнализации сторонним менеджерам мэппинга.

            3. Ну и про DRI ничего не было сказано, хотя, как я сказал, года с 2006-го, X11 - это всего лишь оболочка над DRI для выделения границ окон.


            1. Viknet
              06.01.2024 17:46

              Позволю себе прокомментировать вот этот момент:

              Кстати, именно после появления концепции связки Wayland/Vulkan, Apple задумалась о создании своего собственного низкоуровневого API (Metal). Apple, кстати, даже участвовала в консорциуме Vulcan, потом отвалилась от него.

              Во-первых, Wayland и Vulkan развивались независимо, абсолютно разными людьми и с разными целями.

              Во-вторых, Wayland получился как развитие идеи композитного рабочего стола, которая как раз была реализована за много лет до этого в Mac OS X, а позже и в Windows Vista. Через попытки эмуляции всяких эффектов в Compiz и Beryl на X11, к осознанию того, что тут уже ничего не спасти и пора всё строить с нуля.

              В-третьих, Apple вообще никак не участвовала в создании Vulkan. Скорее неожиданный релиз нового API Metal сразу на реальных устройствах (ну и анонс DirectX 12, конечно) спровоцировал кооперацию по созданию Vulkan:
              "DirectX 12 was announced by Microsoft at GDC on March 20, 2014, and was officially launched alongside Windows 10 on July 29, 2015. "
              "Metal has been available since June 2, 2014 on iOS devices, and since June 8, 2015 on Macs"
              "The Khronos Group began a project to create a next generation graphics API in July 2014 with a kickoff meeting at Valve."

              Ну и, напоследок, Metal не такой уж и низкоуровневый, если сравнивать с Vulkan.


              1. andreymal
                06.01.2024 17:46

                Вы забыли про Mantle, который появился за полгода до всех этих Vulkan/Metal/DX12, и, скорее всего, именно он и послужил пинком к появлению их всех


                1. Viknet
                  06.01.2024 17:46

                  Нет, не забыл. Mantle был проприетарной разработкой AMD для Windows, и в таком виде мало кому был интересен.
                  Маловероятно, что он оказал существенное влияние на Metal (и DX12), именно потому, что был показан всего за полгода до релиза iOS, в которой была полноценная поддержка уже стабилизированного Metal API. Ну и идейно они всё же существенно различны.
                  Просто к тому времени уже все игроки рынка понимали, что существующие API (в особенности OpenGL) слишком многое скрывают за слоями устаревших абстракций, плохо ложатся на архитектуру современных GPU, и в целом не позволяют эффективно использовать аппаратные ресурсы.
                  Поэтому примерно в одно время и началась параллельная работа над современным, более низкоуровневым поколением API: Mantle, DX12, Metal. К сожалению или к счастью, Mantle, как проект AMD, был свёрнут вскоре после показа Metal и DX12, а все наработки были переданы в открытую организацию Khronos Group и послужили основой отраслевого стандарта Vulkan, релизная спецификация которого выйдет через 2 года после этого.


            1. Lsh
              06.01.2024 17:46
              +1

              >Ни DRI, ни Wayland, ни Vulcan не имеют сетевого протокола как такового, потому что это физически нереализуемо с разумным уровнем полезности.

              И это печально. В случае X11 я несколько лет использовал X2Go для работы с удалённым столом в другом городе. Чем то заменить для Wayland? Нечем. Пока никакой VNC не показал такой же отзывчивости.


  1. Batalmv
    06.01.2024 17:46
    -2

    Прочитал с интересом. Я впервые работал с Хми еще в начале 2000х. И тогда я использовал tk/tcl. Надо было по быстрому накидать приложение с "мордой" и решение хорошо зашло

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

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

    --------------

    Тем более задача, я даже не знаю - а зачем? Писать Х-вое приложние на bash???


    1. johnfound Автор
      06.01.2024 17:46
      +6

      Тем более задача, я даже не знаю - а зачем? Писать Х-вое приложние на bash???

      Ну, чтобы было весело. Если вам больше нравится, напишите на tcl.

      Я уже на ассемблере написал и понравилось: подробности здесь - собственно поэтому и понадобилось почитать побольше про X11.


      1. Batalmv
        06.01.2024 17:46
        +1

        Там это не нужно, так как (давно это было), все уже написано до меня.

        Тут куча примеров: Tcl Programming/Tk examples - Wikibooks, open books for an open world

        Язык довольно таки редкий, ка кмне кажется (я про tcl), но дважды вошел в мою жизнь как нечто, на чем совершенно изи можно писать вещи:

        • tk/tcl - когда надо было быстро накидать "морду"

        • expect/tcl - божественное решение для разбора файлов или входного потока

        ------------

        Просто bash - несколько еще более высокоуровневый. Конечно он в общем-то полноценный язык, но все таки это "молоток" очень высокого полета

        Обвязку либо скрипты инициализации/автоматизации - просто незаменим. Но логика на уровне байт/бит - это уже извращение, или на спор


      1. LF69ssop
        06.01.2024 17:46
        +1

        Ну можно было бы пару слов что не так с xlib например.


        1. johnfound Автор
          06.01.2024 17:46
          +2

          Так статья не об этом же.

          А вообще, с xlib мы получаем еще один слой абстракции, который ничего не ускоряет, ничего не упрощает, а только все запутывает. А поверх него городят GTK, и оконных менеджеров, потому что программисты ничего не знают про X11 и не могут его напрямую использовать.


          1. LF69ssop
            06.01.2024 17:46
            +1

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

            Понятно что когда этих слоев несколько то разговор уже другой. Но xlib?


            1. johnfound Автор
              06.01.2024 17:46
              +1

              Ну только в качестве примера – я понял что ID окна генерируется на стороне клиента только когда начал копаться в протоколе. До того думал, что XLib присылает запрос CreateWindow, а сервер делает окно и возвращает его идентификатор. Так происходит в Windows и так выглядит что происходит в xlib. В xlib еще не ясно какие функции ждут ответ от сервера, а какие нет. Да, если теперь, когда научил сам протокол начну использовать xlib, все будет намного яснее, но зачем, если знаю протокол?


  1. Einherjar
    06.01.2024 17:46
    +2

    А протокол в своей сути прекрасен. Он лаконичен и почти совершенен.

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


    1. johnfound Автор
      06.01.2024 17:46
      +7

      абслютно неспособен к сколько либо адекватному развитию

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

      И я не думаю, что это относится только к X11. Весь современный софт такой. Просто не весь софт стоит в самой основе пирамиды. Я точно подозреваю, что подобные процессы идут в MS Windows уже десятки лет, но там конечно ничего не видно, так как все шито-крыто.


      1. Einherjar
        06.01.2024 17:46
        +2

        И я не думаю, что это относится только к X11. Весь современный софт такой

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


        1. johnfound Автор
          06.01.2024 17:46
          +2

          На х11 без библиотек-обертки типа gtk... ну вы сами знаете.

          Ну, как раз я то знаю – как по мне получилось хорошо. Кстати, версия для Windows написана как раз на WinAPI, а для Линукс именно на чистом X11 без каких либо библиотек.


          1. Einherjar
            06.01.2024 17:46
            +2

            а для Линукс именно на чистом X11 без каких либо библиотек

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


        1. Cfyz
          06.01.2024 17:46
          +1

          Справедливости ради, касаемо окон и пр. WinAPI это будет аналог оконного фреймворка, например того же GTK.

          Если бы Linux мог себе позволить иметь один единственный официальный фреймворк для GUI, было бы намного проще.


      1. vkni
        06.01.2024 17:46
        +1

        Скорее всего люди которые его придумали ушли.

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

        Дальше "работа закончена, проект закрыт". А поддерживать пошли "дураки с инициативой" - см, к примеру, расширение XRender https://habr.com/ru/articles/148954/ , посмотрите, какие там примитивы, и насколько криво они ложатся на нужды графики (см отличную картинку с трапециями).


  1. alex-open-plc
    06.01.2024 17:46

    Первые строчки статьи, и... диссонанс.
    В протоколе X11 клиент это то, что обслуживает подключения и выполняет основную работу приложения. Сервер же то, что видит пользователь и с чем по сути работает. Т.е. понятия клиент и сервер как бы "вывернуты".

    Сервер может быть и например на вынь. Xvming
    Цитирую; Xming is the leading X Window System Server for Microsoft Windows®

    Издержки автоматического перевода?


    1. johnfound Автор
      06.01.2024 17:46
      +7

      В протоколе X11 клиент это то, что обслуживает подключения и выполняет основную работу приложения. Сервер же то, что видит пользователь и с чем по сути работает. Т.е. понятия клиент и сервер как бы "вывернуты".

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


      1. alex-open-plc
        06.01.2024 17:46
        -4

        Я скинул ссылку, где конкретно написано что есть что. Не вводите читателей в заблуждение если сами не разобрались...


        1. johnfound Автор
          06.01.2024 17:46
          +3

          А что я написал не так??? Не виляйте а напишите прямо.


  1. Cheater
    06.01.2024 17:46

    Утилит под клавиатурную подсистему иксов (Xlib XKB) я писал немерено, но так низко по протоколу не спускался))

    Я так понимаю в Unix сокет /tmp/.X11-unix/X0 (в простейшем случае когда подключение к Xorg происходит локально к дисплею :0 в однопользовательской системе) непрерывно пишут все X-клиенты и они же непрерывно читают из него? Это же десятки сообщений в секунду если например двигать мышью. Каким образом клиенты делят сокет и не получают чужих ответов?

    Пакет из байтов создать дело нехитрое, но ответ сервера из сокета у меня прочитать не получается:

    #!/bin/bash
    
    # Part 1: Auth message
    display=$(cut -d. -f1 <<< $DISPLAY)
    
    byte_order="\x6c"
    noop="\x00"
    major_ver="\x0b\x00"
    minor_ver="\x00\x00"
    auth_proto_len="\x12"
    auth_data_len="\x10"
    auth_proto="MIT-MAGIC-COOKIE-1"
    auth_data=$(xauth list | grep "$HOSTNAME/unix$display " | awk '{print $3}' | xxd -r -p)
    
    message="${byte_order}${noop}${major_ver}${minor_ver}${auth_proto_len}${auth_data_len}${auth_proto}${auth_data}"
    
    echo -e -n "$message"  # | nc -U /tmp/.X11-unix/X0  как прочитать ответ
    


    1. allter
      06.01.2024 17:46
      +3

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

      К ТС тема для освещения: что такого особенного в X11-меню: где зарыты грабли - в самом протоколе или в Xlib/тулкитах, что во время активации меню некоторые вещи недоступны?


    1. AndreyAf
      06.01.2024 17:46
      +1

      там нормальный unix сокет с listen/accept


    1. johnfound Автор
      06.01.2024 17:46

      Мне кажется, что через nc это не получится. Надо открывать сокет и читать из него наверное через read. Ну и писать через echo.


      1. Cheater
        06.01.2024 17:46

        nc -U пишет в Unix socket как раз. В баше нет builtin для операций с unix sockets, не представляю как реализовать двунаправленное чтение/запись в рамках одного process id на чистом баше не написав код на C.

        Это надо как-то хитро вызывать внешнюю утилиту чтобы она соединяла сокет и дескрипторы ввода-вывода, но у меня не получилось опять же

        mkfifo myfifo; socat PIPE:myfifo UNIX-CONNECT:/tmp/.X11-unix/X0; ./gen_x11_auth.sh > myfifo; cat myfifo
        


        1. johnfound Автор
          06.01.2024 17:46

          В принципе, unix сокет должен вести себя как файл. То есть можно было просто открыть и потом читать и писать через дескриптор. Но возможно я не прав – не очень силен в bash. Я поэтому и такой челендж придумал, чтобы попрактиковаться. :D


    1. kmeaw
      06.01.2024 17:46
      +5

      #!/bin/bash
      
      display=${DISPLAY%.*}
      
      byte_order='6c'
      noop='00'
      major_ver='0b00'
      minor_ver='0000'
      auth_proto_len='1200'
      auth_data_len='1000'
      auth_proto=$(echo -n 'MIT-MAGIC-COOKIE-1' | xxd -p)
      auth_data="$(xauth list | grep "$(hostname)/unix$display " | awk '{print $3}')"
      
      message="${byte_order}${noop}${major_ver}${minor_ver}${auth_proto_len}${auth_data_len}0000${auth_proto}0000${auth_data}"
      
      u=$(mktemp -u)
      v=$(mktemp -u)
      mkfifo $u
      mkfifo $v
      
      nc -U /tmp/.X11-unix/X0 <$u >$v & pid=$!
      trap "rm -f $u $v; kill $pid" EXIT
      
      xxd -r -p <<<$message >$u
      hexdump -C $v


      1. johnfound Автор
        06.01.2024 17:46
        +1

        О! Это выглядит отлично! И работает тоже отлично.


      1. feelamee
        06.01.2024 17:46

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


  1. sim31r
    06.01.2024 17:46
    +1

    Я подумал что речь о протоколе X10, один бит разницы и совсем другая тема )


  1. Wesha
    06.01.2024 17:46

    Аффтар, за что ж Вы русского языка-то так?.. Начал отмечать ошибки, чтобы отослать, но на третьем десятке плюнул...


    1. johnfound Автор
      06.01.2024 17:46
      +13

      Ну, вы уж извините, иностранец я. Если написал бы на хорошем болгарском вам от этого легче было?


      1. Wesha
        06.01.2024 17:46
        -4

        Ой, ну тогда извините, для иностранца очень даже неплохо, но в следующий раз отдайте, пожалуйста, текст на вычитку носителю языка, потому что сейчас полное впечатлнение, что текст написан какой-то школотой — у меня кровавые слёзы из глаз текут.

        Могу Вам вычитать, но уже не сегодня.


        1. johnfound Автор
          06.01.2024 17:46
          +7

          у меня кровавые слёзы из глаз текут.

          Вы там берегите себя. Закапайте что нибудь, а то недолго инфекцию внести... :D


    1. Praksitel
      06.01.2024 17:46
      +2

      А я, наоборот, порадовался. Из многих ошибок стало понятно, что автор - не носитель русского, однако пишет на нём. Что может быть прекраснее? И так и оказалось :)


      1. Wesha
        06.01.2024 17:46

        Из многих ошибок стало понятно, что автор - не носитель русского

        К сожалению, в наше время первым предположением правильнее будет "автор — безграмотное школоло, которое все эти "жы-шы пышы с буквой ЫЫЫЫ" мимо ушей пропустило, ибо было сильно занято в телефоне", и, увы, это предположение в 99% окажется верным. Наш зарубежный коллега в данном случае попал в тот самый 1%, и седую голову пеплом я уже посыпал.


  1. AndreyAf
    06.01.2024 17:46
    +2

    Visual это какое-то абстрактное представление экрана в котором я так и не разобрался.

    это 32бит идентификатор на формат графических данных поддерживаемый X11 системой


  1. e-boroda
    06.01.2024 17:46
    +1

    Отличная статья. Не освещённы некоторые клиент-серверные особенности, неизвестные широкой публике. Например, возможность получения по сети такого ресурса как шрифты.


    1. Lsh
      06.01.2024 17:46

      Это как? А выше пишут, что шрифты на сервере должны быть. Да и я сам помню, что в старых GNU/Linux системах надо было настраивать сервер шрифтов.


  1. Komei
    06.01.2024 17:46
    +3

    Во-первых, огромное, человеческое спасибо за статью. Очень и очень нужная информация.

    Во-вторых, по поводу "Писать ли дальше": хотелось бы точно такого же уровня для Wayland. Всё же X11 уже устарел.


  1. VladimirFarshatov
    06.01.2024 17:46
    +1

    Спасибо за статью, хорошо написано. У меня только один вопрос: Ну почему, хорошие, обзорные, полноценные, доходчивые статьи, описания и пр. выходят непосредственно "к" или даже "после" заката той или иной технологии? Так было с СР/М, так было с MS-(DR-, Free-) DOS, Novell 3.12, и продолжается по сию .. почему нельзя сразу к новому продукту, протоколу, пакету писать ПОЛНОЦЕННУЮ документацию?


    1. Dima_Sharihin
      06.01.2024 17:46
      +3

      Потому что разработка хорошей документации к продукту сопоставима по сложностью с разработкой этого самого продукта. Серьезно


      1. VladimirFarshatov
        06.01.2024 17:46

        Это никак нельзя делать одновременно?


        1. Dima_Sharihin
          06.01.2024 17:46
          +1

          Можно сначала написать документацию, а потом закодировать её. Это "водопадная разработка" получится. Одна проблема - практически не встретишь людей, которые смогут наванговать с первого раза идеальную архитектуру ПО, чтобы она сразу оптимально решала все возложенные на нее задачи.
          Без такого человека архитектура эволюционно развивается, утрясаясь в циклах рефакторинга. Получается, что документация довольно быстро становится неактуальной, пока ПО развивается. А вот когда оно вышло в стабильный релиз - можно и документацию начать писать. Если на это есть деньги и время


          1. vtb_k
            06.01.2024 17:46
            +1

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


            1. Dima_Sharihin
              06.01.2024 17:46
              +3

              Да-да, а в С++ из хидеров генерится документация посредством Doxygen. Речь про нормальную документацию, объясняющую что к чему, зачем, реальными примерами использования и прочим.


          1. VladimirFarshatov
            06.01.2024 17:46
            +2

            Это не более чем оправдашки. К каждому методу, переменной можно прикрутить детальный комментарий в любом языке. Рефакторинг кода, тут да, согласен - требуется синхронная корректировка комментов. Многие (практически все?) современные языки генерят доку из комментов. Проблема не в отсутствии документации, а в идиотизме, который она представляет.

            К примеру, берем пакет на скажем Golang. Кто мешает вначале пакета разместить краткую аннотацию "что это, зачем оно и для кого/чего"? Вместо этого, в аннотации пакета гигантский дисклеймер "автор вася пупкин, ни за что не отвечает и никаких претензий, но донатить можно сюда" вкупе с лицензией..

            К методу, функции .. кто мешает поместить описание параметров и их ограничения? Кто мешает сослаться на зависимость через банальный@seeсмотри_сюда?

            Сам 40 лет пишу такие комменты к каждому своему пакету. Ещё НИ РАЗУ ни один "наследник" не обратился за "расшифровкой".. что это, зачем это, как это .. примеры? Да запросто: пакет покрывается тестами, комментируем в том же стиле, чем не пример использования?

            Вот это, мне не понятно .. ты же сам, через пару-тройку месяцев забудешь всё и будешь серфить свой же код со словами "какой дурак это писал?", не? ;)


            1. Cfyz
              06.01.2024 17:46
              +4

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

              Что код/функция/модуль делает можно худо-бедно понять по самому коду. Куда важнее информация зачем вообще этот код нужен, почему код делает именно так, а не очевидно более простым образом и в каком направлении его можно менять, а в каком лучше не стоит -- пробовали, не получилось.

              Но такие комментарии парой строк перед функцией уже не напишешь и такую информацию очень сложно поддерживать в процессе более-менее активной разработки.

              Вот и получается, что описаний функций с аргументами полно, а логику и практики применения можно найти только в статье или вообще монографии опытного пользователя.


              1. VladimirFarshatov
                06.01.2024 17:46

                Напишешь. Как раз про это и вопрошаю.. да, сопровождать при рефакторингах приходится, но это как раз и решает проблему, кмк.

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

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

                Описание методов, при наличии полноценного описания классов .. зачастую правильный нейминг решает задачу, и требуется только описание параметров и их ограничений. Далеко не всё можно ограничить классом, типом параметра, есть семантические ограничения.. но они как раз зачастую .. отсутствуют!

                Свойства, переменные константы .. да всё то же самое. Чем детальнее описание "выше уровнем" тем меньше требуется уточнений.

                В среднем, у меня на файл класса, пакета уходит до 30% от количества строк на подобное документирование "сверху вниз" и по факту конкретные методы обставляются парой-тройкой строк описания параметров и результата, ибо остальное - выше, в основном в начале файла.


  1. siberianlaika
    06.01.2024 17:46

    Круто, не ожидал увидеть подобную доку еще и на русском в 2024! Хотя, в настоящее время интереснее уже Wayland. X Window конечно продолжают использоваться и вероятно они всё ещё в большинстве на десктопных инсталляциях. Однако, всё новое постепенно переползает в Wayland, а с ним проблема аналогична -- документация вроде как есть, но в таком разрозненном виде, каждая задачка требует лопатить кучу мест и лезть в исходники, чтобы понять как это решать.


  1. romxx
    06.01.2024 17:46
    -2

    Спасибо, конечно, но как-то вы... м-м... подзадержались с вашим рассказом. Лет так на 15. :-]


  1. qw1
    06.01.2024 17:46

    Статья для меня интересна тем, что сам когда-то делал самописные GUI и надеялся подсмотреть какие-то новые идеи. Но статья слишком короткая...

    Кстати, атомы были в винде ещё со времён 3.x и до сих пор живы. Уж не знаю, кто у кого украл...
    https://learn.microsoft.com/ru-ru/windows/win32/dataxchg/about-atom-tables


  1. johnfound Автор
    06.01.2024 17:46
    +7

    Благодаря @feelamee и его эксперименты с Python нашел ошибку в статье.

    В нулевом запросе для подключения есть еще 2 байта для выравнивания после поля auth_data_len.

    Пример после таблицы тоже был неправилен. Теперь все исправлено. Длина начального запроса должна быть 48 байт.


    1. checkpoint
      06.01.2024 17:46

      Длина запроса должна быть 52 байта: 4 байта заготовок запроса и 48 его тело.


      1. checkpoint
        06.01.2024 17:46
        +2

        Блин, я ошибся. Для авторизационного запроса заголовок не нужен. Если его убрать, то мой перловый скрипт работает:

        rz@butterfly:~ % ./perlix.pl
        Connecting to X at UNIX socket: /tmp/.X11-unix/X0, display: 0, screen: 0
        Connection established, x_fd: GLOB(0x82672d678)
        Using Xauth file: /tmp/xauth_PoFPEm
        Xauth: family = 256, x_auth_addr = butterfly, x_auth_number = 0, x_auth_name = MIT-MAGIC-COOKIE-1, x_auth_data = �=������xD"��VZ�
        Xauth: family = 65535, x_auth_addr = , x_auth_number = 0, x_auth_name = MIT-MAGIC-COOKIE-1, x_auth_data = �=������xD"��VZ�
        Xauth cookie found.
        XAuth req sent 48 bytes: 0x6c 0x00 0x0b 0x00 0x00 0x00 0x12 0x00 0x10 0x00 0x00 0x00 0x4d 0x49 0x54 0x2d 0x4d 0x41 0x47 0x49 0x43 0x2d 0x43 0x4f 0x4f 0x4b 0x49 0x45 0x2d 0x31 0x00 0x00 0x83 0x3d 0xfc 0xa4 0x82 0xfc 0x8f 0xc7 0x78 0x44 0x22 0xae 0xdb 0x56 0x5a 0x86 
        Response read 1024 bytes: 0x6c 0x00 0x0b 0x00 0x00 0x00 0x12 0x00 0x10 0x00 0x00 0x00 0x4d 0x49 0x54 0x2d 0x4d 0x41 0x47 0x49 0x43 0x2d 0x43 0x4f 0x4f 0x4b 0x49 0x45 0x2d 0x31 0x00 0x00 0x83 0x3d 0xfc 0xa4 0x82 0xfc 0x8f 0xc7 0x78 0x44 0x22 0xae 0xdb 0x56 0x5a 0x86


        1. checkpoint
          06.01.2024 17:46

          А вот выдача от сервере если неверно указать версию протокола:

          XAuth req sent 48 bytes: 0x42 0x00 0x00 0x0a 0x00 0x00 0x00 0x12 0x00 0x10 0x00 0x00 0x4d 0x49 0x54 0x2d 0x4d 0x41 0x47 0x49 0x43 0x2d 0x43 0x4f 0x4f 0x4b 0x49 0x45 0x2d 0x31 0x00 0x00 0x83 0x3d 0xfc 0xa4 0x82 0xfc 0x8f 0xc7 0x78 0x44 0x22 0xae 0xdb 0x56 0x5a 0x86 
          Response read 36 bytes: 0x00 0x19 0x00 0x0b 0x00 0x00 0x00 0x07 0x50 0x72 0x6f 0x74 0x6f 0x63 0x6f 0x6c 0x20 0x76 0x65 0x72 0x73 0x69 0x6f 0x6e 0x20 0x6d 0x69 0x73 0x6d 0x61 0x74 0x63 0x68 0x00 0x00 0x00 
          Response code: 0
          Error text (25): Protocol version mismatch
          Server version: 11.0
          


        1. checkpoint
          06.01.2024 17:46
          +4

          Окно создать получилось. На вывод текста уже сил нет, спать хочу. :)

          Отображение рудиментарного окна в X-Window без использования библиотек.
          Отображение рудиментарного окна в X-Window без использования библиотек.

          Код на Perl-е:

          Hidden text
          #!/usr/local/bin/perl
          
          use POSIX;
          use Fcntl;
          use FileHandle;
          use Socket;
          
          use Data::Dumper;
          
          my ($host, $display, $screen) = split(/\:|\./, $ENV{DISPLAY});
          
          my $x_fd;
          
          if($host =~ /unix/ || !$host) {
          	my $sock_name = "/tmp/.X11-unix/X".$display;
          	print "Connecting to X at UNIX socket: $sock_name, display: $display, screen: $screen\n";
          
          	socket($x_fd, PF_UNIX, SOCK_STREAM, 0) || die "Can't create socket - $!";
          
          	$paddr = sockaddr_un($sock_name);
          
          	connect($x_fd, $paddr) || die "Can't connect to $sock_name - $!";
          
          } else {
          
          	my $remote = inet_aton($host)  || die "No such host ${host} - $!\n";
          	my $port = 6000 + $display;
          
          	print "Connecting to X over TCP at host: $host, port: $port, display: $display, screen: $screen\n";
          
          	socket($x_fd, PF_INET, SOCK_STREAM, getprotobyname('tcp')) || die "Can't create socket - $@";
          
          	my $paddr = sockaddr_in($port, $remote);
          
          	connect($x_fd, $paddr) || die "Can't connect to $host:$port - $@";
          }
          
          print "Connection established, x_fd: $x_fd\n";
          
          my ($x_auth_fd, $x_auth_file, $x_data);
          my ($data, $read_bytes, $written_bytes);
          my ($x_auth_family, $x_auth_addr_len, $x_auth_addr);
          my ($x_auth_number_len, $x_auth_number);
          my ($x_auth_name_len, $x_auth_name);
          my ($x_auth_data_len, $x_auth_data);
          
          if(-f "$ENV{XAUTHORITY}") {
          	$x_auth_file = $ENV{XAUTHORITY};
          } elsif(-f "$ENV{HOME}/.Xauthority") {
          	$x_auth_file = $ENV{HOME}."/.Xauthority";
          } else {
          	print "Cannot find X auth file!\n";
          	exit;
          }
          
          print "Using Xauth file: $x_auth_file\n";
          
          open($x_auth_fd, "<", $x_auth_file) || die "Failed to open Xauth file: $x_auth_file - $@\n";
          binmode $x_auth_fd;
          
          
          while(1) {
          	$read_bytes = sysread($x_auth_fd, $data, 4);
          
          	if($read_bytes == 0) { ## EOF
          		last; 
          	}
          
          	if($read_bytes < 4) {
          		die "Something went wrong while reading Xauth file, read_bytes: $read_bytes\n";
          	}
          
          	($x_auth_family, $x_auth_addr_len) = unpack('nn', $data); 
          
          	$read_bytes = sysread($x_auth_fd, $data, $x_auth_addr_len + 2);
          	if($read_bytes < $x_auth_addr_len + 2) {
          		die "Something went wrong while reading Xauth file, read_bytes: $read_bytes\n";
          	}
          
          	($x_auth_addr, $x_auth_number_len) = unpack('a'.$x_auth_addr_len.'n', $data); 
          
          	$read_bytes = sysread($x_auth_fd, $data, $x_auth_number_len + 2);
          	if($read_bytes < $x_auth_number_len + 2) {
          		die "Something went wrong while reading Xauth file, read_bytes: $read_bytes\n";
          	}
          
          	($x_auth_number, $x_auth_name_len) = unpack('a'.$x_auth_number_len.'n', $data); 
          
          	$read_bytes = sysread($x_auth_fd, $data, $x_auth_name_len + 2);
          	if($read_bytes < $x_auth_name_len + 2) {
          		die "Something went wrong while reading Xauth file, read_bytes: $read_bytes\n";
          	}
          
          	($x_auth_name, $x_auth_data_len) = unpack('a'.$x_auth_name_len.'n', $data); 
          
          	$read_bytes = sysread($x_auth_fd, $data, $x_auth_data_len);
          	if($read_bytes < $x_auth_data_len) {
          		die "Something went wrong while reading Xauth file, read_bytes: $read_bytes\n";
          	}
          
          	$x_auth_data = $data;
          	
          	print "Xauth: family = $x_auth_family, x_auth_addr = $x_auth_addr, x_auth_number = $x_auth_number, x_auth_name = $x_auth_name, x_auth_data = ".to_hex_str($x_auth_data)."\n";
          
          	if($x_auth_addr eq $host && $x_auth_number == $display) {
          		print "Xauth cookie found.\n";
          	}
          }
          
          close($x_auth_fd);
          
          # Pad auth data
          $x_auth_name = pad_to_32bit($x_auth_name);
          $x_auth_data = pad_to_32bit($x_auth_data);
          
          #my $x_auth_req = make_x_req(1, 0,
          #	pack('CCSSSSSa'.length($x_auth_name).'a'.length($x_auth_data), 
          #		0x6C, 0x00, 11, 0, $x_auth_name_len, $x_auth_data_len, 0, $x_auth_name, $x_auth_data));
          
          my $x_auth_req = pack('CCnnnnna'.length($x_auth_name).'a'.length($x_auth_data), 
          		0x42, 0x00, 11, 0, $x_auth_name_len, $x_auth_data_len, 0, $x_auth_name, $x_auth_data);
          
          
          $written_bytes = syswrite($x_fd, $x_auth_req, length($x_auth_req));
          
          if($written_bytes < 1) {
          	print "X server write error: $!\n";
          } 
          
          print "XAuth req sent $written_bytes bytes: ".to_hex_str($x_auth_req)."\n";
          
          sleep(1);
          
          $read_bytes = sysread($x_fd, $x_data, 1024*100);
          
          if($read_bytes < 1) { 
          	print "Server closed connection unexpectedly!\n";
          	exit;
          }
          
          print "Response read $read_bytes bytes.\n";
          
          my ($x_auth_code) = unpack("C", $x_data);
          
          print "Response code: $x_auth_code\n";
          
          if($x_auth_code eq 0) {
          	## XAuth fail
          	my ($text_len, $x_ver_maj, $x_ver_min, $x_data_len, $text) = unpack("xCnnna*", $x_data);
          	print "Error text ($text_len): $text\n";
          	print "Server version: $x_ver_maj.$x_ver_min\n";
          	exit;
          }
          
          if(!($x_auth_code eq 1)) {
          	print "Unexpected server response code!\n";
          	exit;
          }
          
          ### Parse setup response ###
          
          my ($x_ver_maj, $x_ver_min, $x_data_len, $x_release_number, $x_resourse_id_base, $x_resourse_id_mask,
          	$x_motion_buffer, $x_vendor_len, $x_max_req_len, $x_num_of_screens, $x_num_of_formats,
          	$x_min_keycode, $x_max_keycode, $x_data_rest) = unpack("xxnnnNNNNnnCCxxxxCCx4a*", $x_data);
          
          my $x_vendor_len_pad_size = ceil($x_vendor_len / 4) * 4 - $x_vendor_len;
          
          my ($x_vendor, $x_data_rest) = unpack("a".$x_vendor_len."x".$x_vendor_len_pad_size."a*", $x_data_rest);
          
          print "Server response:
          	x_ver_maj:		$x_ver_maj
          	x_ver_min:		$x_ver_min
          	x_data_len:		$x_data_len
          	x_release_number:	$x_release_number
          	x_resourse_id_base:	$x_resourse_id_base
          	x_resourse_id_mask:	$x_resourse_id_mask
          	x_motion_buffer:	$x_motion_buffer
          	x_vendor_len:		$x_vendor_len
          	x_max_req_len:		$x_max_req_len
          	x_num_of_screens:	$x_num_of_screens
          	x_num_of_formats:	$x_num_of_formats
          	x_min_keycode:		$x_min_keycode
          	x_max_keycode:		$x_max_keycode
          	x_vendor:		$x_vendor
          ";
          
          my $x_format;
          for(my $i = 0; $i < $x_num_of_formats; $i++) {
          	($x_format, $x_data_rest) = unpack("Qa*", $x_data_rest);
          }
          
          my ($s_root_windows, $s_color_map, $s_white_pix, $s_black_pix, $s_cur_input_max,
          	$s_width_pix, $s_height_pix, $s_width_mm, $s_height_mm, $s_min_inst_maps, $s_max_inst_maps,
          	$s_root_visual, $s_backing_store, $x_data_rest) = unpack("NNNNNnnnnnnNCa*", $x_data_rest); 
          
          print "Root windows:
          	s_root_windows:		$s_root_windows
          	s_color_map:		$s_color_map
          	s_white_pix		$s_white_pix
          	s_black_pix:		$s_black_pix
          	s_cur_input_max:	$s_cur_input_max
          	s_width_pix:		$s_width_pix
          	s_height_pix		$s_height_pix
          	s_width_mm:		$s_width_mm
          	s_height_mm:		$s_height_mm
          	s_min_inst_maps:	$s_min_inst_maps
          	s_max_inst_maps:	$s_max_inst_maps
          	s_root_visual:		$s_root_visual
          ";
          
          
          ### Create new window ###
          
          my $main_win_id = $x_resourse_id_base + 1;
          
          $x_create_win_req = pack("CCnNNnnnnnnNN", 1, 0, 8, $main_win_id, $s_root_windows, 200, 200, 300, 300, 10, 1, 0, 0);
          	
          $written_bytes = syswrite($x_fd, $x_create_win_req, length($x_create_win_req));
          
          if($written_bytes < 1) {
          	print "X server write error: $!\n";
          } 
          
          print "Create win req sent $written_bytes bytes: ".to_hex_str($x_create_win_req)."\n";
          
          
          ### Make it visible ###
          
          $x_map_win_req = pack("CCnN", 8, 0, 2, $main_win_id);
          
          $written_bytes = syswrite($x_fd, $x_map_win_req, length($x_map_win_req));
          
          if($written_bytes < 1) {
          	print "X server write error: $!\n";
          } 
          
          print "Map win req sent $written_bytes bytes: ".to_hex_str($x_create_win_req)."\n";
          
          
          ### Read events ###
          
          while(1) {
          	$read_bytes = sysread($x_fd, $x_data, 1024*100);
          
          	if($read_bytes < 1) { 
          		print "Server closed connection unexpectedly!\n";
          		exit;
          	}
          
          	print "Response read $read_bytes bytes.\n";
          
          	my ($x_auth_code) = unpack("C", $x_data);
          
          	print "Response code: $x_auth_code\n";
          }
          
          
          sub make_x_req {
          	my ($cmd0, $cmd1, $req) = @_;
          
          	my $pad_size = ceil(length($req) / 4) * 4 - length($req);
          
          	if($pad_size > 0) {
          		return pack("CCSa".length($req)."x".$pad_size, $cmd0, $cmd1, (length($req) + $pad_size) / 4 + 1, $req);
          	} else {
          		return pack("CCSa".length($req), $cmd0, $cmd1, length($req) / 4 + 1, $req);
          	}
          }
          
          sub pad_to_32bit {
          	my $str = shift @_;
          	my $padded_len = ceil(length($str) / 4) * 4;
          	return pack("a".$padded_len, $str); 
          }
          
          
          sub to_hex_str {
          	my $str = shift @_;
          	my $hex_str = "";
          	for(my $i = 0; $i < length($str); $i++) { $hex_str .= sprintf("0x%02x ", unpack("x$i"."C",$str)); }
          	return $hex_str;
          }
          
          


  1. checkpoint
    06.01.2024 17:46

    Я попытался написать на Perl-е. Запрос на авторизацию формирую, отправляю в сокет, но X сервер тут же закрывает соединение по этому сокету. В логах сервера нет ни слова (делал серверу -logvebosity 20). Поток байтов такой же как в Вашем примере (ну кроме куки разумеется), те же 4 + 48 байта.

    В сети катастрофически мало информации о работе с X напрямую без библиотек, пришлось подглядеть в исходник libXCB.

    Hidden text
    #!/usr/local/bin/perl
    
    use warnings;
    use strict;
    use POSIX;
    use Fcntl;
    use FileHandle;
    use Socket;
    
    use Data::Dumper;
    
    my ($host, $display, $screen) = split(/\:|\./, $ENV{DISPLAY});
    
    my $x_fd;
    
    if($host =~ /unix/ || !$host) {
    	my $sock_name = "/tmp/.X11-unix/X".$display;
    	print "Connecting to X at UNIX socket: $sock_name, display: $display, screen: $screen\n";
    
    	socket($x_fd, PF_UNIX, SOCK_STREAM, 0) || die "Can't create socket - $!";
    
            my $paddr = sockaddr_un($sock_name);
    
            connect($x_fd, $paddr) || die "Can't connect to $sock_name - $!";
    
    	binmode $x_fd;
    } else {
    
    	my $remote = inet_aton($host)  || die "No such host ${host} - $!\n";
    	my $port = 6000 + $display;
    
    	print "Connecting to X over TCP at host: $host, port: $port, display: $display, screen: $screen\n";
    
    	socket($x_fd, PF_INET, SOCK_STREAM, getprotobyname('tcp')) || die "Can't create socket - $@";
    
            my $paddr = sockaddr_in($port, $remote);
    
            connect($x_fd, $paddr) || die "Can't connect to $host:$port - $@";
    }
    
    print "Connection established, x_fd: $x_fd\n";
    
    my ($x_auth_fd, $x_auth_file, $x_data);
    my ($data, $read_bytes, $written_bytes);
    my ($x_auth_family, $x_auth_addr_len, $x_auth_addr);
    my ($x_auth_number_len, $x_auth_number);
    my ($x_auth_name_len, $x_auth_name);
    my ($x_auth_data_len, $x_auth_data);
    
    if(-f "$ENV{XAUTHORITY}") {
    	$x_auth_file = $ENV{XAUTHORITY};
    } elsif(-f "$ENV{HOME}/.Xauthority") {
    	$x_auth_file = $ENV{HOME}."/.Xauthority";
    } else {
    	print "Cannot find X auth file!\n";
    	exit;
    }
    
    print "Using Xauth file: $x_auth_file\n";
    
    open($x_auth_fd, "<", $x_auth_file) || die "Failed to open Xauth file: $x_auth_file - $@\n";
    binmode $x_auth_fd;
    
    
    while(1) {
    	$read_bytes = sysread($x_auth_fd, $data, 4);
    
    	if($read_bytes == 0) { ## EOF
    		last; 
    	}
    
    	if($read_bytes < 4) {
    		die "Something went wrong while reading Xauth file, read_bytes: $read_bytes\n";
    	}
    
    	($x_auth_family, $x_auth_addr_len) = unpack('nn', $data); 
    
    	$read_bytes = sysread($x_auth_fd, $data, $x_auth_addr_len + 2);
    	if($read_bytes < $x_auth_addr_len + 2) {
    		die "Something went wrong while reading Xauth file, read_bytes: $read_bytes\n";
    	}
    
    	($x_auth_addr, $x_auth_number_len) = unpack('a'.$x_auth_addr_len.'n', $data); 
    
    	$read_bytes = sysread($x_auth_fd, $data, $x_auth_number_len + 2);
    	if($read_bytes < $x_auth_number_len + 2) {
    		die "Something went wrong while reading Xauth file, read_bytes: $read_bytes\n";
    	}
    
    	($x_auth_number, $x_auth_name_len) = unpack('a'.$x_auth_number_len.'n', $data); 
    
    	$read_bytes = sysread($x_auth_fd, $data, $x_auth_name_len + 2);
    	if($read_bytes < $x_auth_name_len + 2) {
    		die "Something went wrong while reading Xauth file, read_bytes: $read_bytes\n";
    	}
    
    	($x_auth_name, $x_auth_data_len) = unpack('a'.$x_auth_name_len.'n', $data); 
    
    	$read_bytes = sysread($x_auth_fd, $data, $x_auth_data_len);
    	if($read_bytes < $x_auth_data_len) {
    		die "Something went wrong while reading Xauth file, read_bytes: $read_bytes\n";
    	}
    
    	$x_auth_data = $data;
    	
    	print "Xauth: family = $x_auth_family, x_auth_addr = $x_auth_addr, x_auth_number = $x_auth_number, x_auth_name = $x_auth_name, x_auth_data = $x_auth_data\n";
    
    	if($x_auth_addr eq $host && $x_auth_number == $display) {
    		print "Xauth cookie found.\n";
    	}
    }
    
    close($x_auth_fd);
    
    # Pad auth data
    $x_auth_name = pad_to_32bit($x_auth_name);
    $x_auth_data = pad_to_32bit($x_auth_data);
    
    my $x_auth_req = make_x_req(1, 0,
    	pack('CCSSSSSa'.length($x_auth_name).'a'.length($x_auth_data), 
    		0x6C, 0x00, 11, 0, $x_auth_name_len, $x_auth_data_len, 0, $x_auth_name, $x_auth_data));
    
    
    $written_bytes = syswrite($x_fd, $x_auth_req, length($x_auth_req));
    
    if($written_bytes < 1) {
    	print "X server write error: $!\n";
    } 
    
    print "XAuth req sent $written_bytes bytes: ".to_hex_str($x_auth_req)."\n";
    
    $read_bytes = sysread($x_fd, $x_data, 1024);
    
    if($read_bytes < 1) { exit; }
    
    print "Response read $read_bytes bytes: ".to_hex_str($x_data)."\n";
    
    my ($x_auth_code) = unpack("C", $x_data);
    
    if($x_auth_code == 0) {
    	## XAuth fail
    	sysread($x_fd, $x_data, 1);
    	my ($text_len) = unpack("C", $x_data);
    	print "text_len = $text_len\n";
    	sysread($x_fd, $x_data, $text_len);
    	
    	print "XAuth failed: $x_data\n"; 
    }
    
    
    sub make_x_req {
    	my ($cmd0, $cmd1, $req) = @_;
    
    	my $pad_size = ceil(length($req) / 4) * 4 - length($req);
    
    	if($pad_size > 0) {
    		return pack("CCSa".length($req)."x".$pad_size, $cmd0, $cmd1, (length($req) + $pad_size) / 4 + 1, $req);
    	} else {
    		return pack("CCSa".length($req), $cmd0, $cmd1, length($req) / 4 + 1, $req);
    	}
    }
    
    sub pad_to_32bit {
    	my $str = shift @_;
    	my $padded_len = ceil(length($str) / 4) * 4;
    	return pack("a".$padded_len, $str); 
    }
    
    
    sub to_hex_str {
    	my $str = shift @_;
    	my $hex_str;
    	for(my $i = 0; $i < length($x_auth_req); $i++) { $hex_str .= sprintf("0x%02x ", unpack("x$i"."C",$x_auth_req)); }
    	return $hex_str;
    }
    


    1. johnfound Автор
      06.01.2024 17:46

      Вижу что разобрались, но для читающих – у нулевого запроса нет заголовка в 4 байт. У него только 48 байт когда используется авторизация MIT-MAGIC-COOKIE-1.


      1. checkpoint
        06.01.2024 17:46

        Да, все верно. В 5 утра голова у меня совсем не работает.


  1. Ryav
    06.01.2024 17:46
    +1

    А может кто объяснить, почему до сих пор нет нормального рабочего решения удалённого рабочего стола для Wayland под KDE? Или я может чего-то не знаю.


  1. NickDoom
    06.01.2024 17:46
    +1

    Вот спасибо :) Люблю низкий уровень, как подсел на винапи сто лет назад, так до сих пор все задачи, которые он покрывает (то есть 99.9% задач) им и решаю, единственная прослойка — это стандартный класс диалога номер какой-то-то-там-не-помню, он ещё в DialogBoxParam вызывается. Но это тоже почти винапи :)

    Вот бы сделать поверх иксов точную копию этого набора фишек, был бы эдакий минималистичный брат КуТ — DialogBoxParam for Linux :-D Хотя учитывая то, как глубоко он позволяет в кроличью нору забраться, реализация этого может потянуть за собой всю оконную подсистему из WinE :-D Короче, идея смешная и относится к «ненормальному программированию», но, возможно, что-то в этом есть :)


    1. vkni
      06.01.2024 17:46
      +1

      Эти низкие уровни хороши тем, что их можно почти не менять (если это Win32/X11). Вы написали программу 20 лет назад, а чтобы она достойно работала сейчас, достаточно просто её чуть-чуть подработать. В Win32 манифест надо добавить и, возможно, пару откликов на сообщения.


  1. kt97679
    06.01.2024 17:46

    Интересно, а есть ли в природе x window manager, который вот так на низком уровне взаимодействует с иксами без помощи xlib и прочего?


    1. johnfound Автор
      06.01.2024 17:46
      +1

      Насколько я знаю, нет. По крайней мере те у которых смотрел исходники все используют XLib. Даже не xcb.
      В исходниках GTK и Qt не копался, но кажется, что там тоже будет XLib.


      1. kt97679
        06.01.2024 17:46

        Как вы считаете, насколько сложно было бы адаптировать для прямой работы с иксами оконный менеджер типа dwm?


        1. johnfound Автор
          06.01.2024 17:46
          +1

          Очень сложно сказать. Ведь всё в оконном менеджере завязано на взаимодействие с X. Если это был мой код, то я бы смог переписать. Я с моим кодом такое уже проделывал и не раз. Если чужой код, я бы предпочел написать все с чистого листа. Ведь, написать простенький оконный менеджер по сути не такая уж и сложная задача. А вот понять чужой код, это всегда для меня было очень сложной задачей. Но ведь это про меня. Очень вероятно, у другого программиста, с другим набором умений получиться по другому.

          А с другой стороны – зачем? Если и WM и XLib писаны например на C, то какая разница где именно будет находится код протокола?


          1. kt97679
            06.01.2024 17:46

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


  1. Alesh
    06.01.2024 17:46
    +1

    Хотелось бы ещё сравнения с тем, что сейчас предлагается взамен x11