flipper zero

Что там дальше с Юникодом под Flipper Zero?

Наверняка вам интересно, что там происходит с моим мини-проектом по внедрению Юникода во «Флиппер Зеро»? Шучу, понимаю, что всем пофигу!

Тем не менее всё равно напишу. Проект пока на паузе.

Причина — разработчики прошивки решили обновить фреймворк «U∞g²», который используется для вывода текста. Дело хорошее, но поскольку мои правки зависят от этого фреймворка, придётся подождать его обновления.

Что там в нём изменится мне интересно, можно бы посмотреть, но форсировать не буду, обновится — гляну.

А пока заметил, что переписать придётся ещё одну функцию — elements_multiline_text_aligned. Когда просматривал, не заметил, что в ней используется местный аналог printf, который с Юникодом сроду не работал:

if((start[chars_fit] == '\n') || (start[chars_fit] == 0)) {
    line = furi_string_alloc_printf("%.*s", chars_fit, start);
} else if((y + font_height) > canvas_height(canvas)) {
    line = furi_string_alloc_printf("%.*s...\n", chars_fit, start);
} else {
    chars_fit -= 1; // account for the dash
    line = furi_string_alloc_printf("%.*s-\n", chars_fit, start);
}

Конкретно это место никак не зависит от «U∞g²», можно бы и заняться. Но пока у меня с новогодними праздниками не задалось — всё ещё работаю, плюс немного заболел — горло болит и кашель. Разгребусь, выздоровлю и займусь.

21 комментарий

UTF-8 в elements_scrollable_text_line и elements_string_fit_width

Решил ковать железо, пока горячо — раз дали шанс, постараюсь довести поддержку UTF-8 во «Флиппере Зеро» до ума. Я уже пытался прикинуть объём работ, но при внимательном рассмотрении оказалось, что получившаяся оценка сильно сверху — доработки требуют только три функции, а не одиннадцать.

Тем не менее, поначалу я всё равно приуныл, — там вполне есть где накосячить, значит результат надо тестировать. У «Флиппера» нет эмулятора, получается надо компилировать прошивку, потом заливать её в устройство, делать примеры и уже на них смотреть что пошло не так. Долго и трудоёмко.

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

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

Работу я разбил на два этапа — пока закоммитил только функции elements_scrollable_text_line и elements_string_fit_width. Функцию elements_text_box перепишу попозже, она объёмная, если найдутся замечания по ревью текущего коммита, добью сначала их, так проще.

Скриншот терминала с выводом тестов функции canvas_glyph_width для латинских и кириллических символов, а также вызовами canvas_draw_str и canvas_draw_str_aligned с примерами строк.
Вывод информации стендом для тестирования изменений в функциях АПИ «Флиппера Зеро»

Оставить комментарий

Работа над Юникодом во «Флиппере Зеро»

Вот что значит живой, развивающийся проект. Стоило закоммитить в репозиторий «Флиппера Зеро» своё второе предложение по улучшению поддержки Юникода во «Флиппере», почти сразу его рассмотрели и дали обратную связь. Так далеко не всегда бывает.

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

Комментарий пользователя skotopes: большинство методов библиотеки элементов не готовы к юникоду; PR будет слит, но потребуется дополнительная работа над элементами.
«Most of the methods in elements library are not ready for unicode…»

Я это понял и сам, но, честно сказать, всего за несколько минут до того как получил сообщение от ревьювера. Мои правки пока касались только вывода текста, тогда как во «Флиппере» поверх него работает АПИ, позволяющее выводить ещё и различные элементы интерфейса — окошки, кнопки и тому подобное.

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

< targets/f7/api_symbols.csv awk -F, '$3~/^elements_/&&/char|String/{print$3}'
elements_bubble_str
elements_button_center
elements_button_left
elements_button_right
elements_multiline_text
elements_multiline_text_aligned
elements_multiline_text_framed
elements_progress_bar_with_text
elements_scrollable_text_line
elements_string_fit_width
elements_text_box

До этого всё решалось заменой функций нижележащего фреймворка, тут же проблемы чуть посложнее — функции перебирают строки побайтово, а мне надо их переделать на посимвольный перебор. Какой-то опыт у меня уже есть — натренировался в своих экспериментах с русским языком под «Флиппер».

Не везде, кстати, побайтовый перебор является проблемой. В первой же функции по списку всё будет работать нормально и без правок:

uint8_t lines = 1;
const char* t = text;
while(*t != '\0') {
    if(*t == '\n') {
        lines++;
        uint16_t temp_width = canvas_string_width(canvas, t + 1);
        str_width = temp_width > str_width ? temp_width : str_width;
    }
    t++;
}

Тут идёт подсчёт количества строк и вычисление их максимальной ширины. Для этого функция разбивает последовательность байтов по символу перевода строки — \n. Это работает, так как кодировка UTF-8, используемая тут, устроена так, что символ \n однобайтовый и не может встретиться внутри многобайтовой последовательности.

В других же случаях кое-что придётся довольно существенно переписать, работы предстоит немало.

Оставить комментарий

Вышла прошивка с моим изменением…

Недавно в прошивку «Флиппера Зеро» приняли мой коммит немного исправляющий ситуацию с поддержкой Юникода. Это был пробный камень, я не особо надеялся на успех, скорее ожидал, что завяжется какая-то дискуссия с разработчиками.

Но патч приняли без разговоров, а теперь ещё и релиз вышел. Надо бы продолжать работу, если я хочу, чтобы во «Флиппере» появилась полная поддержка Юникода. Но что-то никак не удаётся собраться с силами.

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

Список изменений и новых участников проекта: обновления NFC, Mifare Classic, HID-приложения, SubGhz; новые контрибьюторы @bolknote, @pborsutzki, @magicnat.
Скриншот списка изменений с моим исправлением АПИ «Флиппера Зеро»

Добавлено: написанием этого поста удалось себя переломить. Сделал нужный коммит, жду реакции.

Оставить комментарий

Для чего обычному человеку Flipper Zero?

Меня просили рассказать немного про «Флиппер Зеро», а именно раскрыть вопрос — для чего это устройство может пригодиться обычному человеку?

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

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

Оранжевое портативное устройство с экраном, на котором видны имена Oleg, Tiki_viki, Xenija, стоит на столе перед чёрным рация Baofeng у окна.
«Флиппер Зеро» и снятые дампы ключей, работающих по протоколу 125 кГц RFID

Туда я запихал всё что можно, начав со своих собственных ключей. Теперь потихоньку собираю ключи от разных других мест, где я иногда бываю. Благо, «Флиппер» умеет дампить всё, что мне нужно — «таблетки», карточки и даже радиосигнал брелка от гаража (у которого скользящий код).

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

Например, я сохранил себе карточку детского центра «Тики-вики», куда дочку иногда водит её няня и теперь без проблем могу сам туда зайти, не дожидаясь, когда няня выйдет ко мне и отдаст карточку.

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

Сдампить удаётся не всё. Недавно я в отеле около Свияжска наткнулся на карточку, которую «Флиппер» успешно записал себе, но при её эмуляции замок не открывался. Не знаю с чем это связано, разбираться не стал.

Ещё «Флиппер» в теории умеет перебирать коды и «взламывать» таким образом подъездные двери и коды шлагбаумов, но на практике мне ни разу не удалось добиться результата — я несколько раз попробовал из интереса. Но глубоко я эту тему не копал, вероятно есть какие-то не совсем очевидные секреты.

13 комментариев

CP1251 во «Флиппере Зеро»

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

Фильм «Узник замка Иф», 1988

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

К сожалению, всё АПИ так устроено, что на вход можно подавать только char, то есть только те символы, коды которых помещаются в байт, в данном случае.

Когда я научился манипулировать шрифтами, которые использует «Флиппер», мне пришло в голову, что если всё, что у нас есть должно поместиться в байт, то почему бы не сделать шрифт в однобайтной кодировке?

Я, конечно, не сомневался, что это заработает, но попробовать не помешало. Много времени не заняло. Взял русский пиксельный шрифт, преобразовал его в формат BDF утилитой otf2bdf, сдвинул русские буквы на их места в кодировке CP1251 и проверил работает ли это на самом деле.

Основной код выглядит следующим образом:

canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_custom_u8g2_font(canvas, silkscr_rus);

char* str = recode_to_cp1251("ВЫВОД РУССКОГО ЯЗЫКА В КОМПОНЕНТ");
elements_multiline_text_aligned(canvas, 0, 0, AlignLeft, AlignTop, str);
free(str);

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

Стилизованный оранжевый гаджет Flipper Zero с экраном, на котором написано: «вывод русского языка в компонент».
Вывод русских букв шрифтом в однобайтной кодировке CP1251

3 комментария

Татарские символы в «Виселице»

Вчера вечером ковырял формат BDF — это формат шрифтов для «Флиппера Зеро». Как я писал была идея добавить в шрифт 6×13 татарские символы, которые там отсутствуют. BDF это текстовый формат, так что это было не слишком сложно.

Символы Ә, Ө и Ү, как и планировал, наскрёб в других шрифтах — поставил «шва», русскую «фиту» и латинскую «уай», просто скопировав пискельные данные букв и изменив метаинформацию. А вот Җ, Ң и Һ пришлось рисовать.

Я сначала понадеялся найти какой-нибудь редактор, который позволит мне это сделать, но не осилил — под «Мак» ничего не обнаружилось. Я перебрал несколько программ, которые должны бы делать то, что мне нужно, но на деле оказались пиксельными редакторами под другие форматы шрифтов.

Интерфейс FontEdit с пиксельным редактором шрифта Monaco 9pt: сетка для рисования, панель символов (включая выделенный символ '/'), информация о размере и количестве глифов.
Я рисую букву Һ (она не на своём месте по кодам, мне это не принципиально)

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

В итоге, я оформил это в виде скрипта, который патчит шрифт, добавляя в него нужные буквы. А то мало ли — вдруг ещё как-нибудь придётся его пересобирать.

Видимо я ещё немного упрощу это место, — после внимательного чтения описания формата в голову пришли кое-какие идеи.

4 комментария

«Дар агачы»

Давно появилась идея прикрутить к моей игре «Виселица» под «Флиппер Зеро» татарский язык. Я всё-таки в Татарстане живу, так что идея выглядит логичной. В выходные делал подготовительные работы в коде — в татарском довольно большой алфавит, я смотрел как всё помещается, немного переделывал меню выбора языков, плюс смотрел есть ли в шрифтах нужные буквы.

Вчера из Института прикладной семиотики АН РТ прислали словарь татарских существительных. Я убрал из него все русские слова (в татарском немало заимствований из русского) и попробовал поиграть.

К сожалению, возникла проблема. В игре я использую два шрифта — 6×12 с нормальным начертанием и 6×13 с жирным. Шрифты готовые, взяты из фреймворка U∞g², я их только переупаковываю, чтобы в них оказались только те буквы, которые я использую в игре.

Оказалось в шрифте 6×13 нет татарских букв! Я этот момент проморгал, думал разница только в размерах. Клавиатуру я печатаю 6×12, поэтому проблему увидел только когда стал выводить татарские слова из словаря (там единственное место, где используется 6×13).

Таблица символов шрифта с латиницей, кириллицей, греческими буквами, цифрами, знаками препинания и диакритическими знаками на чёрном фоне; некоторые символы выделены жёлтым кругом.
Шрифт 6x13B.bdf полностью, выделены буквы, похожие на недостающие татарские

Попробовал посмотреть какие буквы есть в шрифте, чтобы подобрать похожие. Это несложно сделать в командной строке:

awk '/^ENCODING/{print $2}' 6x13B.bdf | xargs -n1 printf '%x\n' |
while read -r line; do printf "\\u$line "; done

Из того что есть нашлись замены для Ә, Ө и Ү, а вот для Җ, Ң и Һ похожих букв не обнаружилось.

Мысли у меня пока две: либо нарисовать недостающие буквы и научиться их добавлять в шрифт, либо обнаруживать ситуацию, когда нужных букв нет в шрифте 6×13 и переключаться на 6×12. Возможно в виде быстрого исправления я выберу второй вариант, а потом попробую реализовать первый.

5 комментариев

Поддержка Юникода во «Флиппере Зеро»

Если помните, какое-то время назад я разбирался как обстоит дело с поддержкой Юникода во «Флиппере Зеро». Если вкратце: плохо, но жить можно. Исследование этой проблемы даже подтолкнуло меня к написанию игры, в которой я мужественно поборол все трудности, связанные с ограниченностью АПИ в этом вопросе.

Видно, что авторы не подумали о поддержке Юникода, поэтому он там работает кое-как и по чистой случайности, просто потому что его поддерживает сторонний фреймворк «U∞g2», используемый для вывода на экран.

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

Две недели назад я подумал — почему бы не попробовать предложить этот патч авторам прошивки. Если не примут, то может быть хоть в дискуссию вступят, всё польза. Скачал разработческую версию ядра, сделал правку и закинул патч.

Сегодня патч приняли без лишних слов. Вот это да, не ожидал! Справедливости ради, это небольшое изменение, которое не должно ничего сломать. Теперь хорошо бы подумать как сделать так, чтобы в остальном АПИ поддержка Юникода заработала. А там, кажется, малой кровью не обойдёшься.

Скриншот GitHub-запроса #3226: пользователь bolknote предлагает добавить поддержку кодировки UCS-2 в функцию canvas_glyph_width, приводя примеры кода для кириллицы.
Внезапно мой небольшой коммит для поддержки Юникода функцией вычисления ширины символа приняли

Оставить комментарий

Расширения GCC

Что-то никак не мог решить для себя — использовать ли при программировании под «Флиппер Зеро» расширения Си или нет. Вроде нужные мне поддерживают оба основных компилятора, — и GCC, и Clang, но какой-то червячок сомнений всё же грыз. Я не вращаюсь в среде сишников и не понимаю как принято, а мне всё-таки хочется писать понятный код, когда нет обратной цели.

Метался-метался, пока внутри исходников прошивки не наткнулся на использования расширения «операторы в выражениях» и сразу успокоился. Раз в прошивке есть, то и мне можно.

Я с первого приложения использую атрибут __cleanup__, но там я ещё скромничал — он используется только один раз. А теперь осмелел и завёл себе макрос с __auto_type, о применении которого мечтал со времён случайного опыта программирования на Си++. Теперь и определения переменных идут стройными рядами, и задумываться о подходящих типах не приходится.

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

Оставить комментарий

«Ahorcado»

Что-то пока мне очень нравится возиться с «Флиппером Зеро» в плане программирования. Руки так и чешутся что-нибудь поделать. В качестве развлечения решил добавить в свою игру для «Флиппера» «Виселица» поддержку испанского, всё-таки второй по распространённости язык в мире, после китайского.

Портативное устройство с оранжевым корпусом и экраном, на котором отображается меню выбора языка: English, Русский, Español.
Конечный результат — английский, русский и испанский

Загвоздка в том, что у меня в игре основной шрифт кириллический — я взял готовый шрифт из прошивки, а в этом шрифте нет «ñ» и других необходимых в испанском символов. Лучшее решение, которое мне пришло тогда в голову, — смотреть что за символ мы сейчас выводим и переключать шрифты, если в текущем нет нужного символа.

Посидел вечерок, всё неплохо заработало. Но когда рассказывал о своём решении братишке, он спросил меня почему я не попытался сделать свой шрифт, где есть все нужные мне символы. Такая мысль приходила мне в голову, но Си я знаю, а как создать свой шрифт для «Флиппера» — нет.

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

Оказалось, это совсем несложно — во фреймворке U∞g2 есть исходники тех же шрифтов, которые использует «Флиппер», и утилита bdfconv, которая умеет их пересобирать. А на специальной странице можно сгенерировать себе необходимую для сборки командную строку.

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

Оставить комментарий

«Виселица»: правлю баги

Игрушка «Виселица» для «Флиппера Зеро», которую мы как-то написали с дочкой, давно перешла из стадии весёлой разработки в стадию унылой поддержки — пользователи потихоньку находят баги, я правлю. Не самая моя любимая стадия, но один баг повеселил.

Один из пользователей пожаловался, что слова с русской буквой «Р» отображаются иногда как-то неадекватно — крайние буквы как будто «срезаются». Выкинув из словаря всё, кроме слов, начинающихся или кончающихся на эту букву, я и сам убедился в наличии этого странного эффекта.

Проблема оказалось в сочетании того как я кодирую символы русских слов и следующей строки кода:

furi_string_trim(line);

Для экономии места и производительности словарь у меня хранится в кодировке UCS-2 с выкинутыми кодами 0x04, из-за чего русская буква «Р» (с кодом 0x0420) записывается как 0x20, а это код пробела.

Вызов furi_string_trim мне нужен, чтобы срезать перевод строки, после её считывания. Только я не учёл, что срезаются там все пробельные символы, включая урезанную до одного байта букву «Р».

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

furi_string_trim(line, "\n");

6 комментариев

«Виселица» в каталоге программ

Нашу с дочкой игру для «Флиппера Зеро» «Виселица» включили сегодня в каталог программ для этого устройства. А ещё она есть в одной из альтернативных прошивок — от разработчика с ником RogueMaster!

Кстати, я думаю, что Ева является самым молодым соавтором игры для «Флиппера» в мире.

Скриншот страницы коммитов в Git: заголовок «Commits», ветка main, дата 7 ноября 2023, коммит «Hangman game (#174)» от bolknote 4 часа назад, статус Verified, хеш de3ef60.
Сегодня приняли нашу с дочкой игру в каталог программ «Флиппера Зеро»

2 комментария

Пилю «Виселицу»

Не то, чтобы я не понимал, что сообщество владельцев «Флиппера» относительно небольшое, всё же это очень специфический инструмент, но всё же не ожидал, что написанная за выходные игрушка удостоится такого внимания.

Только выложил её на «Гитхаб», как мне написал автор одной из популярных альтернативных прошивок, прислал патч, исправляющий некую ошибку и попросил английскую версию, — хочет включить игру в свою прошивку.

Оранжевый портативный терминал с экраном, на котором отображается игра «Виселица» на русском языке: слово «АГА» и алфавит с отмеченными буквами.
Вчера для «Флиппера» пришёл чехол из Китая, выглядит — супер!

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

Для начала переделал сборку на другой инструментарий — ранее игра не собиралась через более распространённый uFBT, потому что мой код использовал кое-какие кишочки прошивки, а uFBT её не содержит. Такой переход поможет опубликовать игру в общем каталоге, но я с этим не спешу.

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

Скриншот терминала с кодом russian.bolk, содержащим числа, шестнадцатеричные значения и сообщения «Окей», «Вы выиграли!», «Вы проиграли!
Файл описания языка, пример для русского

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

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

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

Во-первых, я перешёл на другой генератор — встроенный вызов furi_hal_random_get() вместо обычного для Си random(). Друг, который в прошлом имел дело с микроконтроллёрами, сказал, что как правило лучше использовать то, что предлагает фреймворк — так быстрее.

Во-вторых, я переделал метод выбора случайного слова. Раньше я выбирал слово с линейной сложностью — генерировал номер строки и читал словарь, пока его не достигну. Переделал на константную — выбираю случайное смещение в файле и ищу следующее слово после него. Что дало теоретический положительный эффект — более длинные слова выбираются чаще.

В-третьих, почти в два раза сократил файл словаря. Любая русская буква занимает в распространённых юникодных кодировках два байта, но второй байт всегда одинаковый, его можно выкинуть. Если посмотреть на ряд, описывающий клавиши в моём файле, видно, что первая четвёрка всё время повторяется. То есть из кода русской буквы А — 0x0410 можно убрать 0x04 и оставить только 0x10, мы и так будем знать какую букву кодировать.

В словаре так и сделано. Поскольку внутри программы я храню различные состояния в виде массива, мне нужно знать минимальный получившийся код символа, чтобы вычитать его, преобразовывая код в смещение в массиве. Для русского языка минимальный код будет у буквы А — 0x10, как я показывал выше, то его и нужно записать в файл описания языка. Цифра 16 в нём — это и есть 0x10 в десятичной кодировке.

Надеюсь сегодня допишу всё что нужно и выложу новый вариант.

3 комментария

Написали с дочкой игру

После того как я обнаружил, что на «Флиппере Зеро» можно писать программы с поддержкой русского языка, никак не мог отделаться от желания написать что-нибудь посерьёзнее демонстрационного примера с использованием этой идеи.

Раздумывал недолго, выбрал для реализацию игру «Виселица» (некоторые знают её под названием «Балда»), в которую когда-то мы играли в детстве.

Правила там простые — один игрок загадывает слово и рисует чёрточки по количеству букв в нём. Второй пытается отгадать побуквенно. Если названная буква в слове есть, на бумаге рисуется позиция или позиции этих букв, если нет, то рисуется часть виселицы. Если виселица нарисована полностью, отгадывающий проиграл, если слово угадано раньше, — выиграл.

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

Человек с кудрявыми волосами работает за ноутбуком, на экране которого отображается оранжевый фон с пиксельным логотипом и надписью «РУБРИК».
Дочка рисует виселицу для нашей игры, это первый вариант, в игру не вошёл

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

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

6 комментариев

Пишем на «Флиппере Зеро» по-русски ★

Вкратце: в русским текстом работать можно, но сложно, код лежит на «Гитхабе».

Не мог не обратить внимание, что несмотря на приличное количество русскоязычных авторов программ для «Флиппера Зеро» и российское происхождение устройства, в софте под него нигде не обнаружилось ни одной буквы по-русски.

Flipper Zero

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

На экране Flipper Zero отображается текст: «Привет, мир! Пишем по-русски на Флиппере Зеро. Евгений Степаницев, 2023». Устройство показано в оранжевом стиле на тёмном фоне.
Вывод русских букв (трансляция с экрана через оболочку qFlipper)

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

Внутри «Флиппера» довольно много всякого АПИ для вывода текста, от незатейливого, выводящего строку в заданные координаты, до довольно навороченного, умеющего переносить и выравнивать текст. К сожалению, с русскими буквами оно работать не умеет.

Под капотом у этого АПИ — очень известная в своей нише библиотека U∞g2, у которой по историческим, видимо, причинам есть два набора функций для работы со строками. Одни принимают только однобайтовую кодировку ASCII, другие работают в Юникоде.

АПИ «Флиппера», к сожалению, использует функции, которые с Юникодом работать не умеют. К счастью, по какой-то случайности, упомянутое АПИ содержит два вызова, которые помогают решить эту проблему, правда придётся помучаться.

Первый из них — canvas_set_custom_u8g2_font.

Чтобы что-то написать русскими буквами, нужен шрифт, который их содержит. Стандартный вывод canvas_set_font позволяет выбрать один из нескольких шрифтов, ни одни из которых кириллицу не поддерживает. Тут и приходит на помощь canvas_set_custom_u8g2_font. Он позволяет выбрать произвольный шрифт пакета U∞g2, — внутри их целая куча и некоторые в названии содержат слово «cyrillic».

Вот как я это делаю:

#include <u8g2/u8g2_fonts.c>
// …
canvas_set_custom_u8g2_font(canvas, u8g2_font_haxrcorp4089_t_cyrillic);

Файл u8g2/u8g2_fonts.c внутри себя содержит шрифты в особом формате, можно открыть его прямо в текстовом виде и посмотреть как всё устроено.

Шрифт мы выбрали, теперь надо что-нибудь вывести. Тут на помощь приходит canvas_draw_glyph. «Под капотом» у него лежит u8g2_DrawGlyph, которая понимает символы Юникода почти без танцев с бубном. Думаю, это чистое везение — наверняка эта функция появилась сильно позднее, поэтому у неё нет двух вариантов для работы с разными кодировками.

Плохая новость в том, что строки в коде мы обычно пишем в кодировке UTF-8, а canvas_draw_glyph ожидает их в UCS-2. Но есть и хорошая новость — в прошивке доступно АПИ для перекодирования, как обычно, великолепно документированное на языке Си.

Но canvas_draw_glyph, как можно понять из названия, выводит только один символ. Как же вывести строку? Достаточно просто — надо вывести её посимвольно, сдвигая координату на ширину символа. Звучит просто, но как это сделать?

Тут нас ждёт очередная засада. Дело в том, что canvas_glyph_width, которая должна бы это делать, принимает на вход только char, при том, что нижележащая u8g2_GetGlyphWidth преспокойно работает с Юникодом.

Поэтому я сделал просто — выдрал реализацию u8g2_GetGlyphWidth себе в исходники. В итоге мякотка моего решения выглядит вот так:

void unicode_draw_utf8_str(Canvas* canvas, uint8_t x, uint8_t y, char* str) {
    FuriStringUTF8State state = FuriStringUTF8StateStarting;
    FuriStringUnicodeValue value = 0;

    for(; *str; str++) {
        furi_string_utf8_decode(*str, &state, &value);
        if(state == FuriStringUTF8StateError) furi_crash(NULL);

        if(state == FuriStringUTF8StateStarting) {
            canvas_draw_glyph(canvas, x, y, value);
            x += unicode_GetGlyphWidth(&canvas->fb, value);
        }
    }
}

Тут unicode_GetGlyphWidth — скопированная реализация u8g2_GetGlyphWidth, а furi_string_utf8_decode используется для кодирования UTF-8-строки в последовательные символы UCS-2.

2 комментария

Моя первая программа под Flipper Zero

Вчера вечером разобрался-таки с прокруткой экрана для «песни про пиво» под Флиппер Зеро и выложил всё в репозиторий.

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

Белый многофункциональный инструмент Flipper Zero с оранжевым экраном, на котором отображается текст песни «58 bottles of beer on the wall», и оранжевыми кнопками управления.

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

void beer_render_callback(Canvas* canvas, void* ctx) {
    // …
    elements_scrollbar(canvas, BEER_MAX - pos, BEER_MAX + 1);
    // …
}

int32_t beer_main(void* p) {
    // …
    switch (event.key) {
        case InputKeyDown:
            if (app->pos > 0) app->pos--;
            break;

        case InputKeyUp:
            if (app->pos < BEER_MAX) app->pos++;
            break;

        default:
            break;
    }

    view_port_update(app->view_port);
    // …
}

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

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

2 комментария

Программирую под Flipper Zero

Мне месяц назад на день рождения подарили «Флиппер Зеро» — «перочинный ножик для хакера». Довольно известное в узких кругах устройство, очень занятное. Но об этом как-нибудь в другой раз.

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

Белый многофункциональный инструмент Flipper Zero с оранжевым экраном, на котором написано «99 bottles of beer on the wall», лежит на деревянной поверхности.
Попробую написать «Песню о пиве»

В интернете есть несколько статей о том как начать, которые мне очень помогли. Довольно быстро я научился собирать первое приложение, которые выводит строчку текста и опрашивает клавиатуру. По пути поразился сколько бойлерплейта (шаблонного почти одинакового для каждого приложения кода) пришлось написать. Очень не хватает какого-нибудь фреймворка.

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

4 комментария

← 1 2 →