Pull to refresh

Comments 182

PinnedPinned comments

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

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

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

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

Отображение рудиментарного окна в 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;
}

#!/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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

А Qt и GTK через что работают? не через X11?

Qt, GTK и прочие фреймворки абстрагируют работу с элементами UI. Они предоставляют свой собственный API, а уж с использованием чего он реализован -- приложение, в идеале, знать не должно. В Linux они реализованы поверх X11 и, теперь, ещё и Wayland, в Windows -- поверх WinAPI, в macOS -- поверх Cocoa и т. д.

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

Ну тогда утверждение о прекращении поддержки приложениями явно преждевременны. Получается, любое приложение с Qt под Linux поддерживает X11.

Только если Wayland или что-то другое совсем победит X11, а потом еще и Qt уберет по X11...

Приложения перестанут его поддерживать целенаправленно. При использовании "чистых" Qt, GTK и т. п. все равно что за протокол используется, но полным-полно софта, которому надо или чуть больше, или что-то специфичное, или что-то, что в Wayland пока не реализовано, или просто X дёргает потому что ну он же у всех есть.

Так как Wayland стали ставить и включать по умолчанию, с этим софтом начнутся проблемы. Это криво, то не так -- эти проблемы будут исправляться с обеих сторон, что-то в приложениях, что-то в Wayland. В определенный момент окажется, что из всего дистрибутива XWayland не использует никто и его просто выключат. И вот тут можно будет сказать, что X кончился.

Ключевой момент текущих событий в том, что ещё недавно Wayland был сугубо экспериментальной штукой, которую надо было включать целенаправленно и обычно осознанно. Теперь наоборот это вариант по умолчанию. Если пользователь поставил свежую версию ОС и софт, а он глючит или не работает -- придется править. Чем больше будет исправлено и адаптировано, тем большей дичью будут казаться оставшиеся косяки.

X перестанут использовать осознанно, но его поддержка конечно останется ещё надолго. Только никому уже не будет до этого никакого дела.

В принципе, чем удобен X11, что можно сделать, используя реализации X11 типа nanoX на микроконтроллерах и фактически "тонкие клиенты" сделать ещё тоньше, чем атом с 1Гигом ОЗУ.
Да и возможности Х11 повыше чем огромная куча написанных и тут же брошеных или в неопределенном состоянии находящихся как LVGL и прочие...

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

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

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

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

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

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

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

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

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

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

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

Регулярно читаю как ещё один автор <PROGRAM_NAME> объявил, что не будет поддерживать Wayland, потому что они опять что-то сломали и не хотят чинить. Кажется, недавно вспоминали jitsi и захват рабочего стола или типа того.

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

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

Экспертиза - далёк от иксов, читаю комменты, раз в месяц обновляю свою систему. Идти на до сих пор нестабильный wayland не вижу ни одной причины.

Идти на до сих пор нестабильный wayland не вижу ни одной причины.

а этот нестабильный wayland, он с нами в одной комнате?
Я сижу на sway с 2020 года и ни одной проблемы не видел уже года 2 как.

Я сижу на иксах с 2013 года, у меня vfs буквально сменила 4 компьютера, 3 носителя, 2 разных файловых системы (btrfs оказался мусором). Хомяк частично ещё на трёх рабочих системах. Каждый раз, когда я менял компьютер, я пробовал wayland, обнаруживал что он не решает проблемы, а добавляет новые и выбрасывал. Три года без проблем - это просто ни о чём, btrfs у меня полтора года прожил, пока в кирпич не превратился после потери питания. И да, внезапный переезд с gtk 2 на gtk 3 был большой проблемой.

Почти все проблемы wayland были пофиксены совсем недавно, 2 года назад. А вот проблемы иксов уже никогда не будут пофиксены совсем

Да, уровень поддержки X и Wayland сильно отличается. В одном будут править баги и добавлять современную функциональность, а во втором -- нет.

Приложения, которые не будут поддерживать Wayland, относительно скоро потеряют пользовательскую базу. Да, Wayland далеко не идеал, но X все. Можно сколько угодно сетовать, что в Windows 11 все плохо, телеметрия, кривое поделие с сомнительными решениями, и пытаться сидеть на вылизанной и стабильной XP -- но все, поезд ушел. Сидите сколько хотите, весь остальной мир будет жрать совсем другой кактус.

М-м-м, в мире Линукс вещи происходят несколько по другому. Если есть хоть один человек которому X реально нужен, то он будет существовать и поддерживаться. А скорее всего он будет нужен многим.

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

Грубо говоря дропнут поддержку X в очередном мажорном релизе фреймворка потому что тесты падают, а разбираться и фиксить их некому -- и все. Либо сам пили как хочешь, либо спонсируй, либо просто возьми уже свежую версию и не занимайся ерундой, вот и весь выбор.

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

И конечно «разработчики иксов» это боги с небес и что спустят то и пользоваться будем?

Я же говорю. Все не так работает в мире свободного ПО.

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

Как бы не так, все еще активно поддерживаются например OpenRC, есть дистрибутивы специально для людей которым systemd не нравится.

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

Есть GTK и Qt, но есть же wxWidgets и даже FLTK. И вроде никто из них исчезать не собирается.

Как бы не так, все еще активно поддерживаются например OpenRC,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Порталы уже года 3 существуют

зависит от композитора. Самое универсальное решение, которое есть везде - XDG Desktop Portal, там есть специальное апи для скриншотов и скринкастов. Работает по dbus, показывает диалоговое окно с подтверждением, и т.п.

В композиторах, использующих библиотеку wlroots, есть специальный wayland-протокол, который выдает дескрипторы DMA-буферов с содержимым экрана всем желающим. И мне кажется это правильно. Если юзер запустит шпионскую софтину, то она в любом случае получит доступ к его домашней папке, где хранится самая чувствительная информация. Беспокоиться, что она еще что-то там заскринит - глупо, не? Думаю, повышенная безопасность wayland - это никому не нужный маркетинг, в конце концов и в иксах есть .XAuthority

Большое спасибо за развернутый ответ!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Не правда. Как раз таки X-ы прекрасно работали у меня по модему в 90-е, когда еще всякими RDP и не пахло. И сейчас работают! У меня в загородном доме линк 2Mbit/sec, я частенько подключаюсь от туда к офисной сети по ssh -X и запускаю там 1C-ку на Linux хосте с выводом экрана на свой ноут на FreeBSD и это работает!

Я вот полгода назад запускал поверх SSH X-ы с Firefox - и это адски тормозило. Надо видео?

А по модему - я понмю, как нужно было 10 минут ждать, чтобы Netscape открылся.

У меня Firefox по сети гуляет без проблем. Но в целом с браузерами беда в том, что они грузят в X-сервер кучу всякого шлака (иконки, шрифты и прочее) на все случаи жизни, вот и тормозит при запуске.

Я в 90-е по модему запускал Ansys и симулировал всякую мутотень подключаясь удаленно к SUN Ultrasparc Station.

Возможно у Вас был какой-то крутой модем! На 14400 даже запуск xterm и работа в нем были адом.

Вот сейчас ради прикола пишу вам из Chromium-а с удаленной Linux машины в офисе. Сам сижу дома, на фре. К сети подключен по WiFi на 11g (38Mbits/s). Хромиум вполне юзабелен. А вот Firefox действительно безбожно тормозит на таком соединении.

Помилуйте! 38Mbits/s vs 14400.. Что мы сравниваем? :)

Ну дак времена такие. Тогда и Netscape столько не требовал. :)

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

Я где-то в 2000 году сравнивал - RDP выигрывал. Полгода назад я сранивал опять - X-сервер с Firefox vs виртуалка Forefox и RDP.

Победил RDP.

Просто мои личные впечатления..

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

У меня на ноуте стоит фря, а у бухгалтера - линух. Я частенько захожу по ssh -X и пользуюсь 1С-кой так. Очень удобно!

Wayland - штопаные гон^H^H^Hноски, они эту идеют похоронили!

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

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

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

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

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

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

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

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

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

Полностью согласен. Только не 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 для выделения границ окон.

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

Кстати, именно после появления концепции связки 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.

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

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

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

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

Прокидывать интерфейс по сети никому не нужно

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

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

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

Если будет спрос на чрезвычайно оптимизированный вариант проброса GUI окна или его части -- сделают и для Wayland. Но пока ситуация такова, что чаще всё-таки нужно пробросить рабочий стол или окно ровно как оно есть, со всеми особенностями и нюансами, один в один, и не морочить голову. Ваш сценарий работы может быть очень даже обоснован, но объективно ради небольшого количества человек никто не будет городить всю эту бесполезную канитель с выполнением GUI как будто это web-сервис какой-то на удаленной машине.

Ну и по конкретному сценарию работы. Вы же на локальной машине или хотя бы в локальной сети все крутите если я правильно понял. Зачем тут вообще проброс GUI? Локально есть просто монитор виртуальной машины, в локальной сети любой способ доступа к удалённому столу будет работать прекрасно. Более того, если окно виртуальной машины чем-то не нравится (я просто делаю его нужного размера и разворачиваю внутри окно нужного мне приложения в полный экран), то у VirtualBox и VMware есть seamless mode, который позволяет вытащить окно гостя на рабочий стол хоста.

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

окно ровно как оно есть, со всеми особенностями и нюансами

Так оно и есть. Те же IDE от Джетов или VSCode, Filezilla, GIMP, IncScape, Chrome, Firefox, Libre* выглядят одинаково, проброшенные ли, локальные ли. Сегодня практически весь софт пробрасывается не просто без изменений, а еще и с учетом темы оформления хоста, включая darkmode, если он поддерживается прогой, конечно. По крайней мере с линукс на линукс. С линукс на виндовс - IDE от джетов все еще идентичны на 100%, остальной софт - как повезёт.

Зачем тут вообще проброс GUI?

Затем, что я запускаю виртуалку в "Headless mode", цепляюсь по SSH и работаю только на своем рабочем столе. Я не хочу "переключать компьютеры" по каждому чиху, я не хочу настраивать оболочку виртуалки под себя, чтобы мне было удобно и чтобы она следовала паттернам моего хоста, я не хочу включать фуллскрин виртуалки ради одного окошка, чтобы потом снова переключаться туда сюда, я не хочу использовать "режим интеграции", потому что это багованное нечто, работающее только если звезды сойдутся, плюс оно работает запредельно хреново с интеграцией линукс в виндовс, попутно наслаивая системные панели, отъедая место на экране.

Я хочу просто в терминале вбить

ssh name@host (но не делаю и этого, потому что цепляюсь через менеджер хостов по клику)

А потом, в открытом окошке, просто вбить "phpstorm" или что угодно еще, и просто начать работу.

И все, этот пайплайн будет работать где угодно и как угодно. Он не будет зависеть ни от возможности виртуалки, ни от способа виртуализации в принципе. Он максимально прозрачен и естественен. Ты просто берешь и запускаешь софт, какой хочешь. Мне не нужно настраивать сервера "потоковых прокидывалок", не нужно тратить ресурсы на что-то лишнее, не нужно ничего ставить, не нужно курить мануалы. Нужно терминал открыть и прицепиться по SSH. Keep It Simple Stupid, как говорится. Проверено. Работает.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Утилит под клавиатурную подсистему иксов (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  как прочитать ответ

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

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

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

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

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

#!/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

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

А я почему-то сходу спутал с Х.25 ))

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Отображение рудиментарного окна в 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;
}

Я попытался написать на 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;
}

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

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

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

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

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

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

Именно. Вечные истины :)

А все попытки в кросс-платформенность так и не смогли породить такое же постоянство :(

Поэтому и родилась забавная мысль насчёт DialogBoxParam over X11, чтобы был кросс-платформенным и при этом «вечной истиной» хотя бы в рамках «родной» платформы :)

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

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

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

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

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

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

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

Всегда ли сообщение кратно двойному слову (4 байтам)?

Что если у меня 11 байт, всё равно указывать длинну в 3 двойных слова (12 байт)?

Все запросы и данные выравниваются к двойному слову. Но если в запросе есть какое-то поле длина которого не выравнена, то эта длина всегда указана в какое-то поле запроса как есть.

Вот в том же нулевом запросе, длина строки «MIT-MAGIC-COOKIE-1» 18 символов. Так и пишем в поле auth_proto_len. А потому по сокету передаем 20 байт чтобы было выравнено.

Так что указывать надо реальную длину данных, а передавать надо выравненную.

@johnfoundкак вы считаете, на сколько сложно будет адаптировать вот этот комплект: https://github.com/patrickhaller/no-wm чтобы отвязать его от xlib и перевести непосредственно на x11 протокол?

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

Наверное придется сделать какую-то маленькую библиотеку "микро-xlib" чтобы не повторять одни и те же участки кода.

Но для такого использования это оправдано, потому что нужна очень небольшая часть протокола, а XLib и XCB универсальные библиотеки, которые содержат весь протокол – нужен он вам или нет.

Я гуглил, но так и не смог найти ответа. Поэтому вопрос: в каком формате хранится timestamp в event-сообщении? Максимум, что смог найти, это то, что туда пишется CurrentDateTime. Если я не ошибаюсь (я могу), то в 2038-м году что-то может сломаться.

Формат полностью определен в стандарте:

A timestamp is a time value, expressed in milliseconds. It typically is the time since the last server reset. Timestamp values wrap around (after about 49.7 days). The server, given its current time is represented by timestamp T, always interprets timestamps from clients by treating half of the timestamp space as being earlier in time than T and half of the timestamp space as being later in time than T. One timestamp value (named CurrentTime) is never generated by the server. This value is reserved for use in requests to represent the current server time.

Надо понимать, что значения timestamp генерируются только на стороне сервера, так что конкретный алгоритм не имеет значения. В тех запросов в которых есть поле timestamp, клиент всегда пишет CurrentTime == 0.

Еще надо сказать, что даже если это значение и используется для чего-то, то только на стороне сервера. Я никогда не использовал его на стороне клиента. Зачем его ввели в X мне категорически непонятно. Какая-то перестраховка, что ли...

Даже если и предположить, что клиенту для чего-то понадобится работать с timestamp, то все что он может делать, это сравнивание значения генерированные сервером. Для этого достаточно знать только, что разрешение составляет 1мс и что значения монотонно растут.

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

Спасибо за статью, было интересно. Пока получилось только создать класс в python для удобной отправки запросов и создать с его помощью окно с заголовком. Дальше буду пытаться создать OpenGL контекст и отрисовать в окне треугольник.

Sign up to leave a comment.

Articles