commit -m "better"
3.46K subscribers
1.17K photos
165 videos
3 files
2.6K links
just random thoughts
Download Telegram
Мир динамического связывания, да еще с попытками сохранять ABI, он странный. Иногда я вообще не понимаю, как там хоть чего-то работает, как замышлял автор.

Вот, например:

Compiler stderr:
ld.lld: error: duplicate symbol: xcursor_images_destroy
>>> defined at xcursor.c:249 (/ix/build/fja88msjMiHf4EQC/obj/../src/gdk/wayland/cursor/xcursor.c:249)

>>> xcursor.c.o:(xcursor_images_destroy) in archive /ix/store/fja88msjMiHf4EQC-lib-gtk-4/l
ib/libgtk-4.a
>>> defined at xcursor.c:190 (/ix/build/dZwgGctAuARxPc1P/obj/../src/cursor/xcursor.c:190)
>>> xcursor.c.o:(.text+0x0) in archive /ix/store/dZwgGctAuARxPc1P-lib-wayland/lib/libwayla
nd-cursor.a

ld.lld: error: duplicate symbol: wl_cursor_image_get_buffer
>>> defined at wayland-cursor.c:162 (/ix/build/fja88msjMiHf4EQC/obj/../src/gdk/wayland/cursor/wayland
-cursor.c:162)
>>> wayland-cursor.c.o:(wl_cursor_image_get_buffer) in archive /ix/store/fja88msjMiHf4EQC-
lib-gtk-4/lib/libgtk-4.a
>>> defined at wayland-cursor.c:155 (/ix/build/dZwgGctAuARxPc1P/obj/../src/cursor/wayland-cursor.c:15
5)
>>> wayland-cursor.c.o:(.text+0x0) in archive /ix/store/dZwgGctAuARxPc1P-lib-wayland/lib/l
ibwayland-cursor.a

Что тут написано?

Тут, literally, написано, что авторы gtk4 скопировали кусок wayland(а, может, оба скопировали кусок Xorg, это не очень важно) к себе, и, в случае динамической линковки прикопали эти символы, чтобы они не торчали наружу.

Зачем?

* Понадобилась новая функциональность.

* Совсем леденящий душу пиздец, если они решили, что лично им нада, чтобы этот код вел себя иначе.

К чему это может привести?

Да к чему угодно!

* Иногда это просто работает

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

Тестов на это, конечно, нет, обычно это "я у себя локально проверил, мамой клянусь".
👍11😱5🤡2🍌2
https://bugs.chromium.org/p/chromium/issues/detail?id=1178058#c84

А вот Гугл пытается объяснить, зачем же он отрывает в Хроме поддержку #jpegxl, а, на самом деле, фактически, убивает свой же формат.

Расскажу старый советский анекдот на тему:

Коммунизм. Жена посылает мужа в магазин за мясом. Тот возыращается с пустыми руками и говорит: "А там на двери была табличка: "Сегодня в мясе потребности нет!"
😢4🤣4😁3👎1🍌1
Forwarded from Дидлошная
😁29😐4😢3🥰2🔥1🍌1
commit -m "better"
Беснование в open source набирает обороты, дурацкой лицензией дело не ограничивается, люди уже начинают портить данные на жестких дисках. https://github.com/medikoo/es5-ext/commit/28de285ed433b45113f01e4ce7c74e9a356b2af2 https://github.com/vuejs/vue-cli/issues/7054…
https://sourcehut.org/blog/2022-10-31-tos-update-cryptocurrency/ #ddv #source_hut

sr.ht решили поиграть в модерацию проектов. Пишут, что удалят все проекты, связанные с крипто.

Я тут вижу 2 возможности:

1) Их попросили убрать все это говно, с угрозой закрытия иначе.

2) Они сами решили поиграть в модерацию своей площадки.

В любом их этих вариантов мне далее с ними не по пути, потому что они или врут мне про реальные причины удаления, или они - проклятые леваки-#SJW.
👍7😐63🐳2🍌1
Мне тут посоветовали вместо #mosh использовать https://eternalterminal.dev

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

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

pg-> et pg@pg.vla.yp-c.yandex.net
Could not reach the ET server:
pg.vla.yp-c.yandex.net:2022

pg-> et -u pg pg.vla.yp-c.yandex.net
Segmentation fault

Штоа?

* Падал при любой попытке вывести исключение. Почему?

376   numFrames = backtrace(stack, 
MAX_STACK_FRAMES);
377 memmove(stack, stack + 1,
sizeof(void *) * (numFrames - 1));

Ну, например, потому что у меня backtrace() возвращает 0, потому что без frame pointer вывести стектрейс - дорогое удовольствие, #musl не умеет. 0 - нормальный результат, https://man7.org/linux/man-pages/man3/backtrace.3.html

* Кидал исключение даже до того, как печатал —help. Почему? Потому что, перед тем, как сделать что-то разумное, пытался перенаправить stderr в файл, и, при этом, содержал некорректную реализацию GetTempDirectory() - https://github.com/MisterTea/EternalTerminal/blob/master/src/base/Headers.hpp#L384 (кто так делает, в 21 веке???), при попытке открыть файл в которой летело исключение. Далее смотри пункт 1. Вообще, неправильное получение tmp dir - этим много кто грешит, поэтому я даже запилил микробиблиотечку, которой патчу клиентов по месту - https://github.com/pg83/ix/blob/main/pkgs/bin/et/ix/ix.sh#L26 https://github.com/pg83/ix/tree/main/pkgs/lib/shim/ix

Настраивать логгирование до парсинга command line - это так-то клиника.

Оно у меня снова падает, и я не уверен, что хочу продолжать этот квест.
😁7🔥5👍3🤯1🍌1
commit -m "better"
Мне тут посоветовали вместо #mosh использовать https://eternalterminal.dev Я не знаю, как оно работает, и, весьма вероятно, не узнаю, потому что это какая-то пионерская поделка. Как-то рассказывал про #kitty, вот, примерно из той же оперы - говнокод, работающий…
Ну я раскопал падение. #debug

https://gist.github.com/pg83/a369133c03715179534c8945c2b6afd6

pg-> /ix/store/q2oDAqRXi4JDSoxV-bin-et-ix/bin/et --help
Remote shell for the busy and impatient
Usage:
et [OPTION...] [user@]hostname[:port]

-h, --help Print help
--version Print version
-u, --username Username


Что тут написано?

* username - булева опция
* пытаемся ее получить как строку
* ловим bad_cast в dynamic_cast
* кидаем его, не имея exception handler
* попадаем в std::terminate, который перенастроен библиотекой логгирования
* она пытается вывести в stderr
* у нее не получается(хрен знает, почему)

Там особо красивый сниппет кода:

   2649      std::stringstream reasonStream;
2650 reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]"
2651 << " If you wish to disable 'abort on fatal log' please use "
2652 << "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnF
2652 atalLog)";
2653 base::utils::abort(1, reasonStream.str());


Но abort игнорит текст:

    116 /// @brief Aborts application due with user-defined status
117 static void abort(int status, const std::string& reason) {
118 // Both status and reason params are there for debugging with tools like gdb etc
119 ELPP_UNUSED(status);
120 ELPP_UNUSED(reason);
121 #if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
122 // Ignore msvc critical error dialog - break instead (on debug mode)
123 _asm int 3
124 #else
125 ::abort();
126 #endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
127 }


* и оно все падает, без внятной диагностики
🌚10😢4🔥3🐳2👍1🍌1🍾1
В батле "статическая vs. динамическая линковка" есть один фактор, про который особо никто не рассказывает, ну или мне просто не встречалось раньше.

Это эстетика!

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

Я как-то, когда рассказывал про #mesa, вскользь упоминал про то, как она собирается.

Речь шла про то, что, в процессе сборки 3 .so-шек собирается 10 .a-шек, и они каким-то, достаточно произвольным, образом, объединяются в соответствующие .so файлы, с использованием linker script для сокрытия общих частей.

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

Этого требует эстетика динамической сборки!

Потому что негоже вываливать на пользователя кучу непонятных .so, без какого-то понятного scope, которые делают невесть что. Надо отдать пользователю понятный набор артефактов, с понятным scope, а то, что между ними код дублируется - кого это волнует?

Кстати, ровно в эту же тему можно отнести мой недавний рассказ про дублирование символов в библиотеках #gtk и #wayland. Это же с ума сойти - выделить эти 3 несчастные функции в какую-то совершенно artificial библиотеку, как ее продать пользователю, как опакетить в дистрибутивах, и прочие неприятные вопросы.

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

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

Поэтому внутренняя эстетика, присущая тому или иному способу линковки, на самом деле, диктует то, как код будет разбит на модули, и (мое личное мнение) статическая сборка приводит к более лучшему, отражающему суть кода, разделению.
👍13🤔7🔥5😁1🆒1
https://llvm.org/ пишет, что уже можно в clang 15.0.4
Но на https://releases.llvm.org/ ничего нет.
Это все потому, что в(кстати, как правильно, в или на?) github тег уже отвели, но релиз не выложили - https://github.com/llvm/llvm-project/releases/tag/llvmorg-15.0.4
У меня, конечно, весь дистрибутив уже пересобран 8 часов назад, раньше всех! (это я так хвастаюсь)
🔥8👍3
https://arstechnica.com/tech-policy/2022/09/experts-debate-the-ethics-of-linkedins-algorithm-experiments-on-20m-users/

В статье на серьезных щщах рассуждают, этично ли использовать A/B на кожаных мешках, или нет.

Ну, типа, еслы человек попал в плохой бакет - то это же плохо?

Какой-то левацкий sjw-душок у этого текста, не читайте.
🤯6🤡5🥴3🔥2😁1🖕1
https://kristerw.github.io/2022/11/01/verifying-optimizations/

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

К сожалению, в своей практике мне удалось их применить всего пару раз, и это, кстати, тоже был #Z3.

Зато с удовольствием читаю, как и для чего люди их используют, в надежде, что и мне пригодится.
👍6👌21
commit -m "better"
В батле "статическая vs. динамическая линковка" есть один фактор, про который особо никто не рассказывает, ну или мне просто не встречалось раньше. Это эстетика! Причем не простая эстетика, что, дескать, в случае статической линковки по всей fs не валяются…
#gold

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

Как я уже рассказывал, у меня есть реализация функций backtrace(), на самом деле, их там ажно три штуки:

#include <execinfo.h>
int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *
buffer, int size);
void backtrace_symbols_fd(void *const *
buffer, int size, int fd);

https://github.com/pg83/ix/tree/main/pkgs/lib/execinfo - у меня лежит 4 их реализации:

1) https://github.com/pg83/ix/tree/main/pkgs/lib/execinfo/fake

Это просто реализация, которая ничего не делает(имеет право!), с которой как раз и сломался eternal terminal #et

2) https://github.com/pg83/ix/tree/main/pkgs/lib/execinfo/fp

Это реализация, цельнотянутая из BSD мира, https://www.freshports.org/devel/libexecinfo, широко известна в узких кругах non-glibc linux.

Она содержит фатальный недостаток - если ее позвать из кода, который на x86_64 скомпилен с флагами компилятора по умолчанию(без -fno-omit-frame-pointer), она приводит к падению программы, потому что она падает без frame pointer.

3) https://github.com/pg83/ix/tree/main/pkgs/lib/execinfo/itanium

Эту реализацию наговнокодил я сам, поверх itanium abi для раскрутки стека. Он всегда доступен в программах на C++, а у меня используется #tcmalloc, поэтому по коду это для меня бесплатно. Собственно, она у меня стоит по умолчанию, с возможностью переопределения - https://github.com/pg83/ix/blob/main/pkgs/lib/execinfo/ix.sh Это полезно в цепочке bootstrap, чтобы для первого компилятора собирать поменьше кода - https://github.com/pg83/ix/blob/main/pkgs/bld/boot/8/clang/base/ix.sh

4) https://github.com/pg83/ix/tree/main/pkgs/lib/execinfo/unwind

И реализация поверх библиотеки libunwind от HP. Это старая, известная, библиотека, одна из трех более-менее известных реализаций itanium abi(наряду с stdc++ от gnu, и libunwind от проекта llvm)

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

Что бы я сделал, если бы делал .so для внешнего мира? Я бы скопировал две оставшиеся функции из пункта 2) к себе в проект, и был бы счастлив.

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

Поэтому я делаю финт ушами - https://github.com/pg83/ix/blob/main/pkgs/lib/execinfo/format/ix.sh

Я пересобираю библиотеку из пункта 2, но с указанием, что в .a файле нужно переименовать функцию backtrace(). На выходе я имею библиотеку, в которой есть функции xxx_backtrace(), backtrace_symbols(), и backtrace_symbols_fd()

Если ее скомпоновать с библиотекой 3), или библиотекой 4), то я получу на выходе годный артефакт. Линкер же выкинет при линковке ненужную функцию xxx_backtrace(), и все будет по красоте!

https://github.com/pg83/ix/blob/main/pkgs/lib/execinfo/itanium/ix.sh#L6
https://github.com/pg83/ix/blob/main/pkgs/lib/execinfo/unwind/ix.sh#L6

Собственно, я так и делаю.

В мире динамической линковки это было бы весьма странно.

В случае же статической сборки, если не особенно сильно принюхиваться, то норм, ну и я, заодно, сэкономил себе кучу времени и усилий.
👍8🤔4🔥3
Выходной, почему-то хорошее настроение, поэтому вот вам чутка менеджерской мудрости от дядюшки PG!

(мне эту глубокую мысль донес один коллега лет 17 назад, я не претендую на лавры изобретателя)

Если вы хотите от какого-то коллеги #protaskivanie чего-нибудь, а он активно сопротивляется, рассказывает про иерархию классов о 10 уровней, и про то, что просто так он протащить это не может, и нужен рефакторинг всей системы на пару месяцев, то скажите ему, что вы сейчас положите нужное значение в глобальную(можно пертредную) переменную, и прочитаете ее там, где вам нужно.

Пообещайте, что сейчас зашлете ему PR, и спокойно возвращайтесь на рабочее место.

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

В критической ситуации люди соображают быстро, да.
😁62💩14🔥7🤔2
https://xn--r1a.website/cgevent/4553

"Все чаще нейросети и Blender будут встречаться в одном предложении"

Если подумать, то этот сниппет вполне может стать провидческим.

Довольно легко представить себе Блендер такой площадкой, куда может прийти вендор, и наговнокодить немного кода для демонстрашки своей новой-кленовой технологии.

Ну вот как в #mesa идут Valve и Microsoft и Intel, и так делее.
👍4🤔1
https://vgel.me/posts/mmap-arena-alloc/

"mmap(1Tb): A Rust arena allocator (ab)using Linux overcommit"

Чувак написал memory pool поверх mmap. Как обычно, 2/3 статьи восхваляют Rust, еще 1/3 - некрасивые врапперы над системным примитивом(mmap, в данном случае), и одна строчка реально полезного кода.

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

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

Трюк с выделением 1TB на старте довольно известный, у нас так работает один из боевых аллокаторов в проде.
😁12👍7🥰2
Forwarded from Топ Twitter
😁13👍3🤩2🔥1
commit -m "better"
https://vgel.me/posts/mmap-arena-alloc/ "mmap(1Tb): A Rust arena allocator (ab)using Linux overcommit" Чувак написал memory pool поверх mmap. Как обычно, 2/3 статьи восхваляют Rust, еще 1/3 - некрасивые врапперы над системным примитивом(mmap, в данном случае)…
Мне, пока я писал про memory pool на 1TB, вспомнилась история с одной нашей программой, которая ласково называется "движочек".

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

Как?

char buf[1000000];
MemoryPool pool(buf, sizeof(buf));
someFunc(pool);

В какой-то момент времени заметили, что программа снова утекает.

(почему снова? Потому что программа была сложная, со сложной судьбой, берущей свои начала в C. А санитайзеры с ней тогда еще не умели работать)

Долго с этим ковырялись, в конце концов выяснилось красивое:

Стеки выделялись с overcommit, и переиспользовались. Запросы, которые обрабатывала программа, могли сожрать как 2 килобайта стека, так и 16 мегабайт.

Поэтому, с течением времени, каждый стек из пула обрабатывал "тяжелый" запрос, и "закоммичивался" на свой полный размер.

Выглядело это как утечка.

Как это починили?

Рассматривалось несколько разных решений:

* тред-реклеймер, который бы с каким-то постоянным rate возвращал бы пулы в систему

* В конце каждого запроса бросать кубик, и, с небольшой вероятностью, возвращать(или munmap, или madvise, это неважно) память в систему.

* Завести счетчик использования для каждого пула.

Как конкретно было сделано в движочке, я не помню, коллеги, которые меня читают, если захотят, расскажут в комментариях :)

Я особо хочу заострить внимание на вероятностном способе. Он применим много где, но про него, почему-то, мало кто знает.

Чаще всего его надо использовать, когда у вас горит здесь и сейчас, и проблему надо решить быстро - за единицы и десятки минут. Например, разросся какой-то кеш в программе, и все начало oom-иться - можно добавить в code path на добавление элемента бросание кубика и очистку кеша.

Это, конечно, скорее, про "починить быстро и грязно, чтобы получить время на нормальную починку", но, так-то, знаю места, где подобное грязное решение просуществовало несколько лет, прежде чем появилась нужда в чем-то более качественном.
🔥9👍4🐳3
Forwarded from Дидлошная
😁22🥴4🔥1
commit -m "better"
Прекрасное от того же автора, что и текст про статическую линковку: #gavin https://news.ycombinator.com/item?id=28737554 https://yzena.com/yzena-copyleft-license/ (пункт 16) #copilot Товарищ заодно еще и автор новой лицензии на OSS, основная фишка которой…
https://www.opennet.ru/opennews/art.shtml?num=58055
https://lobste.rs/s/ukgknn/github_copilot_litigation

#copilot

Мне особо нечего добавить к тексту на opennet, он реально хороший, разве что:

* Я не юрист, но, кажется, цитирование скольки-то строк не создает нарушение copyright.

* Чем обучение copilot отличается от обучения меня? Я, когда прочел код, и сам написал похожий, тоже нарушил copyright?

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

* Я, с точки зрения отношения к #strong_ai, луддит, но эту лавину уже не остановить, поэтому зачем ее тормозить - непонятно.
👍5🤩3🤔1🤯1
https://github.com/hanslub42/rlwrap #terminfo

Релиз ничем не примечателен, кроме того замечательного факта, что он у меня сегфолтится.

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

TL;DR - она добавляет readline в любую внешнюю программу, которая ориентирована на построчный ввод. С автодополнением, и прочими ништяками.

Конечно, было бы правильно, если бы этим занимался ваш терминал.

Ну, реально, если бы API терминала был чуть менее асинхронным, и можно было бы сделать синхронный вызов в него. Пишешь в него специальную ESC последовательность, а он тебе - строку с результатом в твой stdin.

Сразу исчезли бы все проблемы с лицензией на GNU readline, и она стала бы доступна по клиент-серверной модели всем желающим.

Вряд ли, конечно, господа терминало-писатели до этого договорятся. #foot
👍6
commit -m "better"
#svg С иконками, я, конечно, намучался. * Пути к дефолтным иконкам в тулкитах. * Какие иконки должны быть. * Часть иконок в Adwaita лежит в png, часть - в svg, эти множества частично пересекаются. Что выберет то или иное приложение, одному (хз знает кому)…
Решил я запилить простенький демон, который бы по запросу вычитывал svg, вызывал бы inkscape, и отдавал бы отренжеренную иконку.

А плагин для отрисовки svg в gtk(gdk-pixbuf) ходил бы в этот демон, вместо того, чтобы вызвать бажную и глючную librsvg.

Но, перед тем, как пилить код, решил проверить, сколько вообще inkscape рендерит простейший svg файл:

pg-> time inkscape -o ~/qw.png -w 100 
window-close-symbolic.svg

real 0m0.160s
user 0m0.147s
sys 0m0.014s

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

(надо сказать, что сам рендеринг там быстрый, но вот cold start inkscape - тормозит)

Думал я, думал, и, в конце концов, плюнул на всю воображаемую красоту, и наговнокодил вот этот скриптец - https://github.com/pg83/ix/blob/main/pkgs/aux/adwaita/icons/png/ix.sh

Он готовит пакет с преренжеренными иконками в png формате, для любой наперед заданной темы, с помощью inkscape.

Все иконки из adwaita, например, заняли 10 мегабайт.

Артефакты рендеринга с таким пакетом иконок, понятное дело, ушли.

Ну, ладно, если попытаться отрендерить некоторые иконки в нестандартном разрешении, они все еще будут некрасивыми, но свои 90% профита я в данном месте съел.

А вы, конечно, сидите на inferior rendering от rsvg, да!
👍8😈3🔥1
commit -m "better"
Меня тут в комментариях спрашивали, что я собираюсь делать с вопроизведением звука через bluetooth, потому что в Linux оно нормально работает только через PipeWire. #cras #sndio Я на это ответил, что у меня есть 2 варианта: 1) alsa умеет предоставлять bluetooth…
ChromiumOS, это, конечно, кладезь полезного кода, жалко что Google не двигает все его запчасти в ширнармассы. Решения инженеров из Гугла мне нравятся больше, чем решения бородатых дядек из RedHat.

https://chromium.googlesource.com/chromiumos/platform2/+/master/vm_tools/sommelier/

Вот, например, совершенно замечательная штука - nested wayland compositor.

Я пока не придумал, куда мне ее применить, но, кажется, что, с ее помощью, можно научиться переживать смерть основного композитора.

Так же она позволяет запустить набор окон со своим уникальным scale.

Написано оно весьма неплохо, если интересно, как запилить свой композитор, то лучше почитать ее, нежели sway/wlroots.
👍9💯1