Наконец-то собрался с духом и переписал ds-measurer со старой грязной вонючей, но такой мягкой тёплой ламповой портянки Perl/GTK3 на новенькую чистенькую металлическую, но суровую холодную транзисторную колбасню C++/GTK4! 😁

Для начала напомню, что за зверь, собственно, ds-measurer, а потом опишу стек, сборки и некоторые весёлости миграции.

Замерщик

В двух словах ds-measurer — экранная мерялка, к которой я до кучи присобачил снятие скриншотов выделяемых областей и пипетку (она же колорпикер, прости Господи). В основном, очень полезна как вспомогашка для несчастных фронтендеров/верстальщиков вроде меня, но не только. Свободная, AGPLv3, на гитхабе. О разных сборках там же см. в конце статьи.

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

Юзверь может измерять то, что у него на экране, собственно, есть — координаты, ширину-высоту, длину от точки до точки, её угол и пр. У каждого измерения (их можно «замораживать», сохранять в стек) есть тоже стек показываемых / не показываемых сеток по разным заданным в YAML-конфиге правилам (например, центр, золотое сечение, трети, пятые и т.д.). Также можно добавлять направляющие, к которым эти измерения могут прилипать. Сами измерения, как текущее, так и замороженные, можно менять и таскать.

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

Что эта адская машина умеет

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

    Само измерение делается мышкой с зажатой, внезапно, ЛКМ, а вот по ПКМ, даже в процессе самого измерения, можно этот самый замер перетащить в более подходящее место. Текущий замер существует, пока нажата хоть одна кнопка мышки. Как только вы отпустили их все, он пропадает, чтобы не мозолить глаза. Если же он вам дорог как память, то можно:

  2. Сохранять замеры в стек. В процессе измерения замер можно сохранить («заморозить», отложить до лучших времён), нажав на клавиатуре кнопочку (по дефолту f). Есть стек таких сохранённых замеров, в котором можно другой кнопочкой (по дефолту Tab) последовательно циклично выбирать активный. Он при этом подсвечивается, и дальше его можно менять так же, как и новосоздаваемый: ЛКМ — размеры, ПКМ — положение на экране, разные хоткеи — показать/скрыть то или иное его значение, сетку, или диагональ с углом. Другими хоткеями можно удалить из стека активный замер, последний добавленный или вообще все (по дефолту, соответственно, Delete, BackSpace, j).

  3. Направляющие и прилипание к ним. Вообще говоря, этих стеков в Замерщике три штуки. Первый — с самими замерами, другой — с «цветными» точками (для каждой выводится список CSS-значений цвета под ней; об этом ниже). А мы сейчас о стеке для направляющих (кстати, по стекам можно и нужно переключаться соответствующими кнопочками, которые можно переопределить, как и почти всё, в YAML-конфиге; по дефолту m — стек замеров, g — стек направляющих, h — стек «цветных» точек).

    Так вот. Как и во всяком уважающем себя графическом редакторе, коим Замерщик не является и являться не должон, предусмотрен механизм направляющих (guides), к которым всё липнет. Что в процессе самого измерения (это когда по ЛКМ), что в процессе перетаскивания замера (по ПКМ), если какая-либо сторона замера приближается к направляющей, всё это дело к ней липнет (радиус подлипания задаётся в конфиге). В общем, как всегда и везде.

    Сами направляющие ставятся хоткеями по месту курсора ([ — горизонтальная, ] — вертикальная). Работа с этим стеком и направляющими в нём полностью аналогична работе со стеком замеров, включая переключение по ним, их перетаскивание (ПКМ), удаление текущей/последней/всех и т.д.

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

    #3b460f
    rgb(59 70 15)
    rgb(23.137% 27.451% 5.882%)
    hsl(72 64.706% 16.667%)
    

    Как и описанный ниже скриншотер, то, куда пипетка засовывает этот текст, зависит от значения ключа target в соответствующей секции конфига: target: clipboard # both | clipboard | stack. both — понятно; и туда, и сюда. clipboard — тоже понятно; в буфер обмена. А stack — это в текущем месте мышки на экране в наш третий стек добавится точка — маленький крестик с этим текстом значений рядом. С этим третьим стеком работа абсолютно аналогична предыдущим двум, с замерами и с направляющими. По этим точкам можно переключаться, таскать их, удалять и т.д. При таскании точки значения цвета под ней, само собой, меняются (при самом таскании, однако, учитывая дороговизну операции забора цвета, пипетка мычит и обновляет цвет только после отпускания ПКМ; можно было, конечно, периодически снимать окрестности, но делать это ради реалтайм-циферек — идиотизм, имхо).

  1. Скриншотер областей замеров. Ну и раз пошла такая пьянка, почему бы нам этот Замерщик не использовать как альтернативу какому-нибудь толстому и зело важному Spectacle-у, скажем? Я вот, например, повесил вызов ds-measurer себе на Ctrl+Alt+z. Нажал, выделил мышкой область, нажал на s (по конфигному дефолту), и скриншот выделенного есть.

    Где он есть — тоже зависит от значения ключа target в соответствующей секции конфига: target: clipboard # both | clipboard | file. both — понятно; и туда, и сюда. clipboard — тоже понятно; в буфер обмена. А file — это песать прямо на диськ в PNG. Куда именно на диськ — указывается паттерном в том же конфиге строкой ниже — file: "~/screenshot_%Y_%m_%d_%H_%M_%S.png". Тут, вроде, тоже всё очевидно. Путь, имя, поддерживаются таймстампы.

    Скриншотить можно прямо во время измерения, при зажатой ЛКМ, можно во время его перетаскивания, при зажатой ПКМ. Скриншотить можно как новосоздаваемый замер, так и выделенный сохранённый.

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

Миграция и стек

О!… Сначала это был скрипт на Perl с подключаемой GTK3-библиотекой. Почему Perl? Потому что умею, и потому что начиналось всё как маааленькая штучка померить ширину-высоту чего-нибудь на экране. Со временем… Ну, вы понимаете. И, в принципе, всё бы ничего, работает — не трожь, всё устраивало, кроме:

…
elsif ($$g{ar} == 90) {
    if ((abs $cursor{dsx} <= $gstate{snap} || $fr || $fc)
    && abs($$S{x0} - $$g{x}) <= $gstate{snap}) {
        $$S{snapped} = 1;
        $$S{x0} = $$S{x1} - $$S{x0} >= 0
            ? floor $$g{x} - $go
            : floor $$g{x} + ($fc ? 0 : $go);
        if ($fc) {$$S{x1} = $x0 * 2 - $$S{x0};}
        if ($move) {$$S{x1} = $$S{x0} + $tw;}
        $cursor{dsx} = $creating
            ? ($$S{x0} - $cursor{x0})
            : ($$S{x1} - $cursor{x1} - $cursor{fdx});
        $cursor{x0i} = floor $$g{x} if $fresh_selection;
    }
    if ((abs $cursor{dsx} <= $gstate{snap} || $fr || $fc)
    && abs($$S{x1} - $$g{x}) <= $gstate{snap}) {
        $$S{snapped} = 1;
        $$S{x1} = $$S{x1} - $$S{x0} >= 0
            ? floor $$g{x} + $go
            : floor $$g{x} - ($fc ? 0 : $go);
        if ($fc) {$$S{x0} = $x0 * 2 - $$S{x1};}
        if ($move) {$$S{x0} = $$S{x1} - $tw;}
    }
}
…

И такого на 756 строк. Я чего-то стал немного во всём этом путаться… Да и сам Perl, глядя на это, тихо плакал в углу.

Во-вторых, несмотря на то, что скрипт на 756 строк и весит всего ~30 килобайт, сам Perl плюс обвязки куда как толще и в рантайме, естественно, медленнее. Да и не у каждого стоит.

Поэтому, когда мне во сне пришла в голову замечательная идея прикрутить к Замерщику ещё одну маленькую фичу, я проснулся, открыл это в Zed, сказал кое-что по-русски, перекрестился и стал писать с нуля на

C++

Я бы, наверно, стал переписывать на C, но слишком уж меня впечатлило то, что только что увидел. Поэтому решил, что у меня будет всё наглухо тупое, полиморфичное и инкапсулированное по самые помидоры. Никто ни о ком ничего не знает, но все друг другом пользуются. Негуманно, знаю. Бездушный рынок, общество потребления. Но надо. Так что ООП, но без фанатизма и сложных иерархий «штука → животная → хордовая → с координатами».

Есть базовые структурки вроде Point, Color и прочие Rect, есть классы вроде Measurement, который знает свою начальную точку Point, конечную точку Point, стек своих сеток Grid (ещё один миникласс), ну и ещё кое-что, умеет всем рассказывать о своих расстояниях, углах, соотношениях сторон, умеет меняться и двигаться, побуждаемый бесцеремонным и максимально декларативным распорядителем в app.cpp, ну и т.д. Чистые математика и алгоритмы.

Есть отдельные модули UI, хоткеев, скриншотера и пипетки. Есть, наконец, товарищи, которые рисуют всё это дело, кто во что горазд, и кто что умеет:

GTK…

Несмотря на то, что я его не очень-то люблю, строить светлое коммунистическое будущее с Qt мне совсем не улыбалось. Долго это, строить светлое коммунистическое будущее. Кроме того, я сразу решил жёстко разделить бизнес-логику и отрисовку, выделив для GTK свою песочницу (ну, хорошо, треть Сахары). Так что сейчас для перехода на другой тулкит понадобится поменять только один .cpp-файлик. Да, примерно на 400 строк со всеми комментариями, но всё же. Он, кстати, самый толстый в проекте.

А. Ну, ещё немножко GTK протекло в скриншоты с пипеткой, но они тоже отделены от основной логики, а ещё и сами разделены в части платформозависимости от wlr-screencopy/org.kde.KWin.ScreenShot2. Тут мы плавно переходим к

Wayland

Пожалуйста, не кидайтесь тряпками. Я сам все глаза выплакал от того, что иксы, давайте будем честны, уходят. Да, есть форки, да, есть хитрые дистрибутивы/окружения. У меня у самого Artix с runit. Да и с KDE я, наконец, слез. Сил моих больше не было. Но… слез не на i3, а на Sway.

Под иксы мерялок есть у нас (вроде). А вот умудриться объяснить вейланду, что мы не пытаемся стырить номер банковской карты с экрана позади, а просто хотим сделать его скриншот… 😁 ну, хорошо, измерить высоту поля ввода. Да ещё и попытаться поселить wlr-screencopy с org.kde.KWin.ScreenShot2 в одной коммуналке так, чтобы они общую борщевую кастрюлю не взорвали…

Я спрятал всю эту чехарду в namespace platform отдельного модуля для скриншотера и пипетки и с помощью сборочных флажков и макросов #if defined(HAS_KDE_SCREENSHOT) и #if defined(HAS_WLR_SCREENCOPY) попросил их не ссориться.

Ну и поверх всего этого — gtk4-layer-shell, чтобы наше вот это вот всё ложилось прозрачным слоем поверх всей системы.

Ну и Cairo

Тут как раньше сомнений не было, так и сейчас не появлялось. Чего тут выдумывать. Прекрасная библиотека для рендеринга всего этого дела в 2D-векторе.

Код и сборки

ds-measurer свободный, под AGPLv3. Живёт на гитхабе: https://github.com/assador/ds-measurer.

Там, собственно, код, похожее на здешнее описание (по правде говоря, я его отсюда туда и скомпоновал) и две сборки в AppImage, обе для x86_64. Linux/Wayland, если вдруг…

AppImage

Обе сборки отличаются только в части скриншотера и пипетки. Обе работают и в KDE, и чёрт знает, где ещё, но с wlroots, однако:

  • Первая — ds-measurer-kde-x86_64.AppImage — скриншотит и понимает цвета под курсором только в кедах с их org.kde.KWin.ScreenShot2.

  • Вторая — ds-measurer-wlr-x86_64.AppImage — скриншотит и понимает цвета под курсором только c wlroots с их wlr-screencopy.

При самостоятельной сборке из исходников это определяется флагами ENABLE_KDE_SCREENSHOT и ENABLE_WLR_SCREENCOPY в CMakeLists.txt.

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

А если нужны, кстати, то с KDE-сборкой есть одна весёлая чисто кедовая заморочка: если вы хотите пользоваться скриншотером и пипеткой, вам нужно будет дать бинарнику разрешение для кед. Иначе он просто презрительно сплюнет KWin ScreenShot2 D-Bus error: GDBus.Error:org.kde.KWin.ScreenShot2.Error.NoAuthorized: The process is not authorized to take a screenshot и продолжит как ни в чём ни бывало.

Как ни крути, а куски GTK в AppImage-ы, разумеется, засунуть пришлось, коль скоро всё это встаёт с дивана с его помощью. Так что чистый ручной бинарник Замерщика на сотню с небольшим килобайт распухает в опакеченных AppImage до 30+ мегабайт. Вот, что у них в животиках:

  1. Сам бинарник ds-measurer, собственно

  2. GTK4 и базовый GUI-стэк:

    • libgtk-4.so

    • libglib-2.0.so, libgobject-2.0.so, libgio-2.0.so (GLib-овские штучки)

    • libcairo.so и libpango-1.0.so (графика и текст)

    • libgdk_pixbuf-2.0.so (для скриншотера и пипетки)

    • libharfbuzz, libfreetype, libfontconfig (вёрстка/шрифты)

  3. Layer Shell: собранная отдельно libgtk4-layer-shell.so (для оверлея)

  4. Парсер libyaml-cpp.so (для чтения конфига)

  5. Ассеты ds-measurer.desktop и ds-measurer.svg (ярлык и иконка :o))

Выпилено же из наших картинок и берётся с хост-системы:

  • Wayland и EGL: libwayland-client, libwayland-server, libwayland-egl, libwayland-cursor (предполагается какбэ, что утилитку для Wayland запускают под Wayland)

  • X11: libX11, libX11-xcb (по той же причине)

  • GStreamer: медиа-модуль GTK4 вырезан полностью (когда-нибудь Замерщик во время работы может и будет петь блюзы и показывать в углу непристойные видюшки… но не сейчас)

Так что, AppImage более-менее самодостаточен, включает в себя GTK4, Cairo и gtk4-layer-shell, а от системы требует только запущенный Wayland-композитор с поддержкой wlr-layer-shell (Sway, Hyprland, KDE Plasma, River и т.д.).


Вот. Добро пожаловать, пользуйтесь, критикуйте, ругайте и хвалите.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Нужна ли вам отдельная сборка для X11?
66.67%Нужна2
33.33%Не нужна1
0%У меня ZX Spectrum0
Проголосовали 3 пользователя. Воздержался 1 пользователь.