Но еще сильнее я люблю Turbo Vision. Для меня он — привет из молодости, когда мир был проще, когда люди не таскали туда-сюда могучие фреймворки, задачи на четыре строчки решались четырьмя строчками и никому не приходило в голову рисовать тривиальные картинки с помощью HTML, CSS и джаваскрипта. Когда кнопку можно было поставить в точку X и быть уверенным, что она останется там в любую погоду. Когда задачи не решались методами “скачать” и “установить”, а также “подобрать” и “сконфигурировать”. Когда запрещалось использовать код, у которого “неизвестно, что там внутри”, а любую проблему можно было отладить за конечное число шагов. Когда документация представляла собой книжку, а не распечатку с форума.
Посему, когда у нас однажды встала задача “рисовать на чем угодно”, включая разные ЖК-мониторы, электронную бумагу, удаленные экраны и даже файлы во флэше, и все это из нескольких разных операционных систем, у меня зачесались руки.
Рисовать UI для нас — задача непрофильная и всегда решалась по остаточному принципу, не барское это дело — кнопочки по экрану расставлять. Но, раз приперло, надо делать.
Вы понимаете, о чем я? Да, мы написали свой Qt. Вернее, конечно, не его, поскольку нет смысла переписывать уже написанное.
Мы написали библиотеку для рисования графических интерфейсов, заточенную на горячо любимые нами встраиваемые системы. С условием:
— Чистый С++, никаких фреймворков, никаких зависимостей, сторонних библиотек и подгружаемых модулей, STL в ограниченных количествах, чтобы не изобретать велосипед,
— Никаких “языков конфигурирования” и прочей магии, требующей сотни часов для первичного освоения,
— Минимальная привязка к операционной системе: потоки, мьютексы и sleep, чтобы портировать куда угодно,
— “Память зря не жрать”, ее всегда меньше, чем хочется,
— Рисовать на всем, что умеет выводить пиксель. Цветной, или черно-белый.
За полгода “библиотека рисования котиков”, сначала называвшаяся просто Componentality Graphics, а затем получившая более благозвучное имя JetCat (не путать с производителем авиационных моторов) обросла мясом. Сначала графические примитивы, прозрачность и спрайты. Затем — ViewPort’ы и скроллеры. Потом — растровые шрифты, антиалиасинг и генератор этих самых шрифтов из TrueType (тащить честную растеризацию TTF на встраиваемые платформы ради того, чтобы, зачастую, нарисовать несколько надписей — это как-раз то, что категорически хотелось избежать), затем надписи, рамки, поля ввода, окошки, меню… В феврале мы выпустили первый продукт с JetCat внутри и взялись за работу с удаленными экранами. Благо, ISurface, на котором, собственно, происходит рисование — это интерфейс с двумя методами: plot и peek.
И, когда в дополнение к нескольким разным целевым Линуксам, а также к Windows, под которой так удобно отлаживаться, к Raspberry PI, AllWinner A13 и FreeScale iMX6 JetCat без проблем запустился на OS X, мы решили выложить его в открытый доступ.
Во-первых, наверняка кому-нибудь пригодится.
Во-вторых, возможно найдутся те, кто выявит и исправит ошибки быстрее, чем это сделаем мы, а нам и кроме этого есть, чем заняться.
В-третьих, нам категорически не хватает времени написать документацию.
А главное — хорошая получилась штука.
Разумеется, не Qt. Меньше. Проще. Слабее, пока по-крайней мере. Намного слабее. Зато дешевле, собирается без бубна и заводится с полпинка. Что еще надо для счастья embedded-разработчику, которому поручили расставлять кнопочки по экрану?
Брать и пользоваться можно здесь: https://bitbucket.org/componentality-admin/jetcat
Рядом надо не забыть клонировать https://bitbucket.org/componentality-admin/common-libs, он обеспечивает переносимость.
Примеры на подходе, какая-никакая документация — тоже. Пока можно изучать тесткод… Ну, и там все просто.
Это не реклама. Лицензия MIT. На здоровье, пользуйтесь.
Примечание 1: Дорогое, бесценное Community! Мы очень ценим Вашу критику и Вашу помощь. И мы прекрасно понимаем, как вы хотите иметь примеры, описание, вычищенный код с длинной историей использования и много ASSERT'ов с внятной диагностикой, покрывающих и Вашу конкретную проблему тоже. Все это будет, рано или поздно. Проблема в том, что когда оно будет, Вы уже не будете нам нужны. Вы нам нужны сейчас, когда всего этого нет. Чтобы оно появилось. В обмен мы отдаем Вам большой и довольно ценный (оценки могут различаться) продукт своего труда. Это, в нашем понимании, честный размен.
И еще.
Всем, кто сказал «спасибо» наша огромная благодарность. А тем, кто помогает делать JetCat лучше — благодарность в квадрате. И за нами обычно не ржавеет.
Комментарии (83)
darkAlert
19.04.2016 18:06+1Как то в тексте у вас Qt ассоциируется только с GUI, но это далеко не только GUI…
akk025
19.04.2016 18:07+1Не GUI в задачу не входит. Впрочем… практическая полезность не-GUI в Qt существенно более спорна (спорить не буду), вплоть до вредности.
iroln
19.04.2016 18:21+4Если приложение пишется в основном на Qt, то возможности по работе с файловой системой, сетью, сигналами/слотами, потоками, процессами и т. д. не могут быть вредны, хотя бы потому, что в стандартной библиотеке C++ почти ничего такого не наблюдалось годами. Понятно, что можно использовать Boost и ещё десяток библиотек, но зачем, если всё нужное уже есть в Qt? Особенно учитывая то, что инструменты из Qt зачастую удобнее и проще использовать.
Про вредность не понятно. Никто же не заставляет насильно использовать, например, контейнеры из Qt там, где хочется использовать из STL.akk025
19.04.2016 18:27+4Ради этого Qt оброс тучей зависимостей. И портирование его перестало быть тривиальным (и уж точно выходит за рамки «just compile»). И ресурсов он стал жрать мама не горюй. То бишь перестал быть легким настраиваемым environment'ом для встраиваемых и мобильных решений. Кому-то это годится и даже нравится. Кому-то, особенно тем, кто работает на слабых системах, причем на многих разных малыми ресурсами — это мешает. Вплоть до неприемлемости.
bigfatbrowncat
19.04.2016 18:41Смотрю на свой мобильник… 2ГГц, 4 ядра, 3ГБ, разрешение экрана больше, чем у телевизора… Понятие «встраиваемые решения» как-то размылось в последние годы :)
А если серьезно — огромное спасибо. Как раз нужен легкий GUI под SDL.akk025
19.04.2016 18:45+1Я же говорю, я очень люблю Qt. Просто есть задачи, которые он перерос. А на смену ничего общепризнанного не проросло
VioletGiraffe
19.04.2016 18:39Ох, помню, как портировали Qt-приложение на Андроид. С тех пор — только кросс-платформенное С++ ядро и по возможности «тонкий» UI под каждую платформу, и никак иначе.
На днях как раз выпилил самописный костыль CThread в пользу std::thread. Для работы с файловой системой, к сожалению, пока остаются костыли, хотя они маленькие и простые для понимания (обёртка над WinAPI / POSIX). Ждём std::filesystem.
Antervis
19.04.2016 23:47+1конечно не заставляет. Только вот по большей части методы Qt-шных классов принимают QString'и на входе (вместо кошерных однобайтных std::string) и возвращают QList'ы (коим, если я не вру, нет прямого аналога в STL) вместо std::vector'ов ;)
Мне нравится Qt, но есть у него минус — его нельзя использовать «чуть-чуть». Либо не пользуешься, либо на все 25-30 мб библиотек, которые притянутся с первым же юзом какого-нибудь небогатого на аналоги класса. Со всей втекающей кухнейFiery_Ice
20.04.2016 00:18+4Справедливости ради уточню:
— классы Qt не в воздухе висят, а умеют делать импорт/экспорт из/в соответствующие классы STD. При чем, если говорить за строки — то QString умеет делать импорт/экспорт как из/в std::string, так и из/в std::wstring, так и из/в char* и т. д. А это довольно удобно.
— что до QList. Он действительно не является аналогом std::vector поскольку одно — список, а другое — умный массив. Совершенно разные типы контейнеров. Ну и снова таки: если надо — QList умеет делать импорт/экспорт из/в QVector, который уже является аналогом std::vector, и делает импорт/экспорт непосредственно из/в std::vector.DarkEld3r
20.04.2016 00:41поскольку одно — список, а другое — умный массив
QList не совсем список. В смысле, не такой как std::list — эти массив в котором лежат указатели на элементы "списка". Ну или сами данные, если размер меньше или равен указателю.
Antervis
20.04.2016 07:31я знаю, что их можно конвертировать туда-обратно. Каждая конвертация за линейное время. В итоге проигрыш в производительности еще больше, причем на ровном месте.
QtRoS
19.04.2016 22:56+1Кстати да, я первым делом подумал, что статья будет про самописный механизм сигналов и слотов — то, что делает кьют Qt'ом.
akk025
20.04.2016 12:57https://bitbucket.org/componentality-admin/common-libs/src/5213740f65af950313f6a214c1e2ea335b25d3cc/common/common_events.h?at=master&fileviewer=file-view-default
Несмотря на простоту, он заслуживает отдельной статьи.QtRoS
20.04.2016 13:12+1Довольно простое решение, мы даже когда-то хотели что-то наподобие на C реализовать на старой работе для своей GUI библиотечки.
akk025
19.04.2016 18:59-1Поставил ссылку на репозиторий https://bitbucket.org/componentality-admin/common-libs, извинения всем, кто пытался собрать без него.
Еще раз — документация и примеры будут вместе с официальным анонсом… Но, поскольку это побочный и бесплатный результат основных и платных работ, черт знает, когда это произойдет. Скорее всего в середине мая.
iamwizard
20.04.2016 02:39+1Немножечко не работает:
$ gdb ./TestCode
Program received signal SIGSEGV, Segmentation fault.
(gdb) bt
#0 0x00007fff8a2ac434 in pthread_mutex_lock () from /usr/lib/system/libsystem_pthread.dylib
#1 0x00007fff8219cf4d in closedir () from /usr/lib/system/libsystem_c.dylib
#2 0x000000010001c5c7 in CST::Common::listFiles (dir=...) at ../../common-libs/common/common_utilities.cpp:342
#3 0x000000010000d87b in Componentality::Graphics::LoadableFontSet::deserialize (this=0x7fff5fbff628) at ../Graphics/Fonts/FontSet.cpp:328
#4 0x0000000100000b7d in main (argc=, argv=0x1100004) at ../Graphics/TestCode/TestCode.cpp:23
OS X 10.11dreamer-dead
20.04.2016 12:39+4Видимо, потому, что на Вашем Маке нету вот такого пути
const std::string FONT_PATH = "c:\\Componentality\\Fonts";
А затем там DIR* dir == nullptr отдается в closedir(), чего делать не следует.
Сколько там еще таких подводных камней, никому не известно.
Тестов же нету от слова совсем.
Здесь вот утечка памяти.
А что там с блобами происходит, вообще непонятно: там есть аллокации, а кто память освобождать будет — непонятно.
Итого — RAII в полной мере нет, тестов нет, баги есть. Использовать не стоит, по моему.akk025
20.04.2016 12:47+1Большое спасибо за найденную утечку. Пофиксим. Как и все остальное, что будет находиться. Если найдете много — заплатим премию. Ибо оно нам важно.
akk025
20.04.2016 12:49+1Мы, собственно, затем и выкладываем, что сами не успеваем. А премию можем заплатить уже сейчас, если Вы принимаете Яндекс деньги или PayPal. И благодарность однозначная.
dreamer-dead
20.04.2016 13:04Я Вам рекомендую просто собраться с ASAN\TSAN и найти кучу всего совершенно бесплатно.
А тесты стоит написать хотя бы ради себя же, начать с самых примитивных вещей.
Потому что если нет доверия базовому слою, то как с ним работать?akk025
20.04.2016 13:36Кучу всего мы уже нашли :)
Что-то Вы помогаете нам найти. Что-то найдем в ближайшем будущем.
Про «как работать» вам могут рассказать разработчики Android — у них до сих пор в драйверах NULL pointer assignments вываливаются… И все живы.
Я не к тому, что это хорошо, а к тому… Дописал примечание в конце текста.
akk025
20.04.2016 12:50Память у блобов освобождает использующий через вызов purge().
dreamer-dead
20.04.2016 13:01+1Ну так в том и смысл RAII, что ответственность с головы разработчика по освобождению ресурсов переносится на компилятор\стандартную библиотеку, у которых степень доверия гораздо выше.
А как иначе понимать, у кого нужно дергать purge() а у кого нет?
blob initial(100500); blob copy1(initial); blob copy2(copy1); // Что тут делать?
Вы хотели дешевое копирование блобов, я понимаю.
Но разгребать потом вручную, где и что освобождать — не хочется.akk025
20.04.2016 13:30У нас встраиваемые системы. На них, зачастую, нет ресурсов для лишних операций. Поэтому да, разработчику приходится напрягать голову больше, чем на мощных платформах… Но там у него есть другие методы разработки UI.
К счастью, в JetCat API блобов нет. Поэтому заморачиваться поднятыми Вами вопросами не придется.dreamer-dead
20.04.2016 14:59+1Я не знаю, что конкретно Вы имеете ввиду под JetCat API, у меня всего пара минут была на поглядеть.
Но `git grep blob` показал, что блобы таки используются в коде внутри JetCat.
Вот и вопрос — можно ли быть уверенным, что никто там не забыл дернуть вручную purge()?
Вместо того, чтобы отказываться по идейным соображениям от инструментов вроде умных указателей, можно было бы просто взять профайлер. Я уверен на 99%, что он покажет места типа этого, а не вызов инлайнового дестурктора умного указателя. Сколько раз и зачем там делается копия строк?
Нельзя было исходным блобом обойтись, расширить Base64 чтобы он мог работать с типом blob?
А тут точно все хорошо? В самом конце на ровном месте делается реаллокация + копия строки на операторе + (в худшем случае).
Поверьте, корректная и быстрая программа — не антонимы. И не обязательно для этого использовать ручное управление ресурсами.akk025
20.04.2016 16:22К сожалению, формат «быстрых комментариев» не подразумевает долгих дискуссий. Но, хотя теоретически вы правы, в данном случае нет. Во-первых в том, что критикуете на основании пары минут изучения, это уже не очень хорошо и профессионально. Во-вторых потому, что из-за «во-первых» не учитываете, как применяется то, что критикуете. Например, блобы используются только внутри JetCat и риск того, что программист, его использующий, забудет их деаллоцировать отсутствует. Впрочем, это, честно говоря, вполне разумный и приемлемый риск. Base64 используется только при загрузке шрифтов, соответственно, реаллокация мало на что влияет, равно как и то, на что вы сослались, неверно предположив, что мы не умеем пользоваться профайлером (этот код выполняется однократно).
Но да, оптимизировать можно. Ради таких комментариев мы и работаем с коммьюнити. В любом случае это не криминал. Ну а корректные программы существуют только в сознании академических организаций. В коммерческом секторе это понятие неприменимо, поскольку существует ограничение бюджета и ограничение времени разработки.
akk025
20.04.2016 13:33-1По той же причине в JetCat не используются смарт-указатели, исключения и другие ресурсоемкие конструкции. Это не хорошо, и не плохо, это инструмент для решения определенного класса задач. Для других систем можно делать UI на HTML или Flash. Если вы относитесь к секте, которая считает, что managed code спасет мир — это просто не ваш вариант. Если вам иногда приходится писать на C++ под STM32, то Вы понимаете, о чем я.
GarryC
21.04.2016 09:47+1Позвольте с Вами не согласиться по поводу ресурсоемкоти.
Я недавно специально потестил умные указатели под IAR для M3-M4 и при дОлжной настройке они не стоят НИЧЕГО по сравнению с локальным указателем. Совсем ничего, то есть код использования абсолютно не различим, а конструктор и деструктор гарантировано вызываются.
Вот с исключениями хуже, опять таки при нормальном исполнении они не стоят ничего в плане времени, но создают МОРЕ кода для собственно обработки, что особенно обидно, если она практически не вызывается.
akk025
20.04.2016 12:56Видимо, потому, что на Вашем Маке нету вот такого пути
const std::string FONT_PATH = «c:\\Componentality\\Fonts»;
/var/componentality/fonts
Туда надо положить шрифты.dreamer-dead
20.04.2016 13:18Это ладно, лучше поправьте передачу nullptr в closedir()
Там много таких мест, где не проверяется указатель _dir.
Иначе может крешиться не только в тестовом коде.
artjomtro
20.04.2016 15:59-2Вы уж простите, но я даже прослезился ;(
Скрытый текстНо еще сильнее я люблю Turbo Vision. Для меня он — привет из молодости, когда мир был проще, когда люди не таскали туда-сюда могучие фреймворки, задачи на четыре строчки решались четырьмя строчками и никому не приходило в голову рисовать тривиальные картинки с помощью HTML, CSS и джаваскрипта. Когда кнопку можно было поставить в точку X и быть уверенным, что она останется там в любую погоду. Когда задачи не решались методами “скачать” и “установить”, а также “подобрать” и “сконфигурировать”. Когда запрещалось использовать код, у которого “неизвестно, что там внутри”, а любую проблему можно было отладить за конечное число шагов. Когда документация представляла собой книжку, а не распечатку с форума.YemSalat
20.04.2016 19:00На github не планируете выложить случайно? Там больше народу, мне кажется проще будет помощь найти.
( ПС я не удержался, выложил пока: github.com/YemSalat/jetcat )
Lauren
22.04.2016 13:09Баг в common_events.cpp:
16: Event& event = *mQueue.front();
17: mQueue.pop_front();
Самое смешное, что этот код даже будет работать, если не добавляется новое элемент в очередь.
«Во-вторых, возможно найдутся те, кто выявит и исправит ошибки быстрее»
Для этих целей не помешало бы включить баг трекинг.dreamer-dead
22.04.2016 16:11+1В чем баг?
В очереди хранятся сырые указатели |Event*|
Затем оттуда достается указатель и делается pop_front()
Пока не сделают delete event, все будет в порядке, нужно будет просто вовремя удалить объект.
akk025
22.04.2016 23:00Да-да, в чем баг?
Lauren
23.04.2016 16:11Прошу прощение за дезинформацию. Не обратил внимание на тип хранимых данных и воспринял код как:
16: Event& event = mQueue.front();
17: mQueue.pop_front();
Не однократно замечал подобные ошибки, вот мой внутренний анализатор и выдал ложное срабатывание.
А в чём смысл разыменования указателя в ссылку, а потом преобразования ссылки обратно в указатель?
CTAPOBEP
23.04.2016 15:36+1> В-третьих, нам категорически не хватает времени написать документацию.
Тогда, боюсь, никто в здравом уме не будет пользоваться Вашей библиотекой. Многолетний опыт разработки привел меня к паттерну «для всего нового смотрим сначала объем и качество документации». Кроме того, вижу явное непонимание того факта, что «сделать для себя» и «сделать для всех» это соотношение объемов работы примерно 1 к 5.
tmnhy
Понятно, времени не хватает… Но пару скринов?!!!
Gorthauer87
Да и хотя бы hello world тоже не помешал бы.
akk025
Есть, лежит в папочке тесткодов.
Gorthauer87
А как это чудо компилировать? makefile говорит, что не хватает файлов.
akk025
Каких именно? Мы тут вырезали то, что «не для посторонних», могли криво отрезать. Пишите — пришьем.
akk025
Собрал на всякий случай. Собралось. Вроде всего хватает.
Gorthauer87
aleksey@Aleksey ~/d/t/j/Make (master)> make
make: *** Нет правила для сборки цели «../../common-libs/common/common_events.cpp», требуемой для «../build/common_events.o». Останов.
akk025
А вы common_libs склонировали? Он лежит в репозитории рядом и обеспечивает переносимость.
Gorthauer87
А где написано? И почему не субмодулями тогда?
akk025
Из-за особенностей конфигурационного менеджмента проектов-доноров. Сейчас напишем.
Gorthauer87
Тогда напишите скрипт сборки, а лучше не выпендриваться, а приделать хотя бы CMakeLists.txt или ещё что-нибудь.
Кстати, makefile явно приболел.
akk025
mkdir ../build вас спасет.
Gorthauer87
Не, мне кажется рановато вы выложили, нужно было хотя бы добиться того, чтобы этим можно было пользоваться без необходимости подключать телепатические способности.
akk025
Возможно.
iassasin
Простите, но чтобы понять, что означает «Fatal error: can't create ../build/common_events.o», вам нужны телепатические способности? Понимаю, попасться может не конкретно данный случай с данным сообщением, но здесь вроде бы самое первое, что приходит в голову — проверить папку build и права доступа на нее.
Gorthauer87
Да, но для публичной библиотеки крайне важно, чтобы ей было легко пользоваться и не делать никаких лишних телодвижений.
Вот посмотрите на cargo rust'овский или go, там просто достаточно указать библиотеку или репозиторий в качестве зависимости и начинать смотреть примеры.
akk025
Они не сразу такими стали. Помню, сколько сил ушло на то, чтобы получить что-то функциональное из Android 1.5, там из коробки не работало вообще ничего.
В данном случае мы дарим вам (не вам конкретно) хорошую штуку в обмен на готовность делать телодвижения. Для тех, кому важно их не делать, это предложение пока не подходит. А когда их не надо будет делать… возможно нам будет неинтересны такие подарки.
Gorthauer87
А в Linux обо вообще работать должно? Запускаю TestCode и ничего не происходит.
akk025
Посмотрите ему в код — он пишет в .bmp файл. А вообще должно, лучше проверять на messagebox.
Gorthauer87
c:\temp\FONTS
А это вообще нормально? Оо
akk025
Для виндового тесткода?
murzilka
У меня папка temp на диске D. Думаете, я один такой?
akk025
Нет, не думаем. Думаем, что букву в коде поменять несложно.
Gorthauer87
Есть же переменные окружения, можно через них спросить где искать tmp.
Gorthauer87
А на нем написано, что он виндовый? Зачем он тогда в Linux собирается, если даже теоретически не должен работать.
akk025
За исключением путей ему все равно. На нем проверяли (это же тесткод) работоспособность нижних слоев и в Линуксе тоже.
Еще раз — вы требуете от тесткода, на котором «на ходу» отлаживались разработчики, чтобы он обладал функционалом примеров к продукту. Это требование вас не портит, тем не менее, оно некорректно. Для остального — дописал примечание в текст. Если бы все было оформлено хорошо и гладко, было бы легко работать и не было проблем, то зачем бы мы отдавали все это в коммьюнити?
Gorthauer87
Потому что таких проектов много, недавно вот новость про очередной была, а тратить время на разбирательство не хочется. В итоге пользуются в основном лишь хорошо документированными проектами. Их же и допиливают под свои нужды.
Да, ещё хорошим тоном считается использование CI какого-нибудь типа travis'а.
akk025
Ну, значит, это не ваше. Проектов, наверное, действительно много, мы, правда, когда искали, ни одного не нашли.
GeorgeIV
Под виндой не компилится, 4 проекта, входящих в solution, отсутствуют. common-libs по ходу зря снесли, на него все остальное завязано.
akk025
Ничего не снесли, все лежит в соседнем репозитории.
GeorgeIV
К сожалению, модерация не поспевает ). Скачал (хотя это неочевидно, как отмечали выше), скомпилил, запустил — жуткое мерцание экрана при ресайзе. После этого сразу захотелось закрыть. Вещь, может быть и хорошая, но очень сырая, по-моему, вы поторопились.
akk025
Вообще-то ресайза там нет, внутри окна Windows (которое тесткод, даже не пример) — рисунок фиксированного размера. Т.ч. при ресайзе поведение может быть любым, ибо это не пример реализации resize'able интерфейса.
Просьба трактовать testcode именно, как тесткод.
akk025
А приведенные картинки сняты именно под виндой. Будут примеры — расшарим. Продуктовую реализацию, увы, подарить не можем.
lexkazakov
И заветный «hello world!», пожалуйста! Или readme.md.
akk025
Пока могу предложить смотреть TestCode'ы. Примеры на подходе, выложим.
akk025
Резонно. Выложил.
evnuh
Ну раз уж презентуете свой продукт, сделайте уж то, что хочет видеть большинство — скрины и примеры кода как этого добиться для типичных контролов для типичных ОС. Вот как эти ребята, например: https://github.com/vurtun/nuklear
akk025
А мы не презентуем продукт. Мы делимся с сообществом побочными результатами работы.
zagayevskiy
А, то есть, «типичный Open Source»? «Е*итесь, как хотите»?
akk025
Пока так. Если увидим, что кому-то нужно и интересно — будем оказывать поддержку.
DeXPeriX
Спасибо огромное за ссылку на Nuklear. Давно ищу такой продукт — чтоб и public domain, и кроссплатформенный, и для чистого Си, и чтоб вес приложения не сильно раздувал.