Операционная система Haiku: портирование приложений и создание пакетов

Осенью этого года, спустя 6 лет разработки, вышла первая бета-версия «R1/beta1» операционной системы Haiku. Я давно слежу за этим интересным проектом, который нацелен на воссоздание и последующее развитие существовавшей в 1994–2000 годах системы BeOS. Поэтому, как только на новостных IT-сайтах я увидел новость о выходе бета-версии Haiku, я незамедлительно решил посмотреть, что же было добавлено в этот долгожданный релиз. После установки системы в виртуальную машину VirtualBox и небольшого ознакомления с её основной функциональностью, я подумал, что было бы неплохо немного помочь OpenSource-сообществу, которое сегодня развивает эту операционную систему. Начать я решил с того, в чём у меня накопился небольшой опыт: с портирования некоторых игровых проектов.

aweoqyqziwcmq_qcwirvpf6drno.png


Рабочий стол операционной системы Haiku.

Позже я попытался доработать некоторые уже существующие приложения и библиотеки. Именно этой моей небольшой деятельности в различных репозиториях с открытым исходным кодом и будет посвящена эта статья. В ней я последовательно опишу те проблемы, с которыми столкнулся и расскажу про методы их решения. Большинство патчей, которые были сделаны в процессе этой работы, я попытался отправить в upstream существующих проектов, дабы обеспечить в них поддержку Haiku и заинтересовать их разработчиков существованием альтернативных операционных систем.
Операционная система Haiku использует гибридное ядро, которое представляет собой реализацию микроядерной архитектуры с возможностью динамической подгрузки необходимых модулей. Оно базируется на форке ядра NewOS, которое было разработано бывшим инженером Be Inc., Travis’ом Geiselbrecht’ом. Сегодня этот разработчик работает в Google над ядром, которое называется Zircon, для новой операционной системы Google Fuchsia, но это уже другая история. Итак, поскольку разработчики Haiku декларируют бинарную совместимость с BeOS, то они вынуждены поддерживать не две привычных всем архитектурных ветки, а три: x86_64, x86 и x86_gcc2. Последняя архитектура представляет собой груз совместимости с компилятором старой версии GCC 2.95. Именно благодаря ей имеется возможность запуска приложений, написанных для оригинальной операционной системы BeOS. К сожалению, из-за этого груза совместимости, разработчики Haiku не могут использовать современные возможности языка программирования C++ в системных API. Тем не менее, установочные образы они подготавливают только для двух архитектур: x86_64 и x86. Всё дело в том, что дистрибутив Haiku для x86 является гибридным: несмотря на то, что все системные компоненты собраны под x86_gcc2 для обеспечения бинарной совместимости, пользователю предоставляется возможность установки или сборки любых современных приложений, которые были сделаны с расчётом на современные компиляторы и архитектуру x86. Дистрибутив Haiku для архитектуры x86_64 является полностью 64-битным и не имеет возможности запуска 32-битных приложений BeOS и Haiku. Однако, совместимость на уровне API имеется, поэтому если у вас есть на руках исходный код приложения под BeOS или Haiku x86, вы без проблем сможете скомпилировать его под Haiku x86_64 и всё должно работать. Образ операционной системы под архитектуру x86_64 рекомендуется для установки на реальное железо, если вам не требуется поддержка каких-либо специфичных приложений BeOS или 32-битных приложений Haiku.

Стоит сказать, что в этой операционной системе имеется частичная поддержка стандарта POSIX. Этот фундамент делает её родственной UNIX-like системам и позволяет легко переносить их программное обеспечение. Основным языком программирования является C++, он активно используется, поскольку публичные API у Haiku в основном преследуют объектно-ориентированную парадигму программирования. Тем не менее, никто не запрещает использовать и язык программирования C, только для большинства случаев придётся писать соответствующие прослойки совместимости. Программный интерфейс операционной системы сгруппирован в отдельные системные фреймворки, которые отвечают за ту или иную возможность, например, за интерфейс или поддержку сети. Это немного похоже на то, что имеется в macOS или во фреймворке Qt. Обязательно нужно отметить, что эта операционная система является однопользовательской, хотя некоторые подвижки в сторону обеспечения многопользовательского режима работы у разработчиков Haiku имеются.

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

twcmqedw1ofbempjx2ktaxjesmi.png


Продвинутое управление окнами в операционной системе Haiku: поддержка тайлинга и вкладок.

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

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

Содержание:


1. Пакеты и репозитории в Haiku
2. Первые шаги: портирование игры Adamant Armor Affection Adventure
3. Доработка существующего порта NXEngine (Cave Story)
4. Портирование игры Gish
5. Проект BeGameLauncher, позволяющий быстро создавать лаунчеры для игр
6. Портирование Xash3D: легендарная игра Half-Life и официальные дополнения
7. Портирование двух частей игры Serious Sam: The First Encounter и The Second Encounter
8. Портирование игры Вангеры (Vangers)
9. Реализация диалогов в библиотеке SDL2 для Haiku
10. Портирование моего форка программы Cool Reader
11. Доработка программы KeymapSwitcher
12. Заключение

1. Пакеты и репозитории в Haiku


По сравнению с оригинальной BeOS, в Haiku появилось значимое нововведение: система управления пакетами, которая включает в себя различные инструменты для получения и установки программного обеспечения из различных источников. Такими источниками могут служить официальные репозитории Haiku и HaikuPorts, неофициальные репозитории и просто отдельные и специально подготовленные HPKG-пакеты. Подобные возможности по установке и обновлению ПО давно известны в мире Unix-like операционных систем, теперь же вся их мощь и удобство успешно добрались и до Haiku, что не может не радовать рядовых пользователей этой операционной системы. Благодаря выстроенной вокруг пакетного менеджера инфраструктуры, теперь любой разработчик может легко портировать новое или доработать уже существующее приложение с открытым исходным кодом, затем добавить результаты своего труда в репозиторий портов программного обеспечения HaikuPorts, после чего они станут доступны всем пользователям Haiku. В итоге получившаяся экосистема напоминает таковую у операционных систем macOS с их Homebrew, FreeBSD с их портами, Windows с MSYS2 или Arch Linux c его AUR’ом.

Инструмент для сборки пакетов и портирования программного обеспечения, называемый HaikuPorter, поставляется отдельно от операционной системы и устанавливается по небольшому мануалу, расположенному в репозитории на GitHub. После установки этой утилиты с того же GitHub’а скачивается всё дерево рецептов, над которым и работает разработчик. Рецепт представляет собой обычный Shell-скрипт с инструкциями, по которым HaikuPorter и будет собирать требуемый HPKG-пакет. Примечательно, что сам инструмент написан на языке программирования Python 2, тесно взаимодействует со существующей системой управления пакетами, а для фиксации изменений исходного кода ПО и генерации набора патчей, внутри себя использует стандартный инструмент — Git. Именно благодаря подобному стеку технологий делать рецепты для сборки HPKG-пакетов и наборы патчей к ПО в виде patchset-файлов очень удобно и просто. В большинстве случаев мне пришлось использовать всего три команды при работе с HaikuPorter’ом:

alias hp="haikuporter -S -j4 --get-dependencies --no-source-packages"

hp libsdl2
hp libsdl2 -c
hp libsdl2 -e


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

Таким образом, чтобы опубликовать какой-либо пакет в репозиторий HaikuPorts и сделать его доступным для всех пользователей Haiku, разработчик должен установить у себя HaikuPorter, развернуть дерево рецептов, локально собрать HPKG-пакет и протестировать его, затем сделать коммит в свой форк дерева рецептов, после чего оформить Pull request на GitHub’е. Опубликованную работу должны рассмотреть разработчики Haiku, после чего они принимают решение влить ваши изменения в репозиторий или же отправить их на доработку. Если изменения приняты, то такой же HaikuPorter, установленный на сборочном сервере, удалённо соберёт пакет и автоматически опубликует его в репозиторий.

В бета-версию «R1/beta1» операционной системы Haiku была добавлена специальная программа HaikuDepot, которая позволяет работать с пакетами и репозиториями через графический интерфейс пользователя, а не через консольные команды в терминале.

bv7dbtkhvrdz4nunutpahn4kate.png


Программа HaikuDepot, запущенная в операционной системе Haiku.

Благодаря этому инструменту неискушённые и начинающие пользователи Haiku могут удобно управлять своей пакетной базой. Стоит отметить, что это приложение не просто является GUI-оболочкой над существующим пакетным менеджером, но и реализует дополнительную функциональность. Например, авторизированные пользователи могут выставлять оценки и писать отзывы к доступным для установки пакетам. Кроме того, у HaikuDepot имеется специальный сайт Haiku Depot Web, позволяющий просматривать изменения пакетной базы в интернете или скачивать отдельные HPKG-пакеты.

<< Перейти к содержанию

2. Первые шаги: портирование игры Adamant Armor Affection Adventure


После того, как я ознакомился с функциональностью операционной системы в виртуальной машине VirtualBox, я решил оценить работу библиотеки SDL2 в ней и портировать на Haiku игру Adamant Armor Affection Adventure, о переносе которой на платформу Android я писал ранее. Сборка программы не потребовала каких-либо изменений исходного кода, я просто установил из репозитория все нужные инструменты, библиотеки, их заголовочные файлы и выполнил следующее:

cmake -DCMAKE_BUILD_TYPE=Release -DGLES=off -DANDROID=off -DCMAKE_C_FLAGS="-D__linux__" -DSDL2_INCLUDE_DIR=`finddir B_SYSTEM_HEADERS_DIRECTORY` -DSDL2_MIXER_INCLUDE_DIR=`finddir B_SYSTEM_HEADERS_DIRECTORY` ../aaaa/src/main/cpp
cmake --build .


Поскольку в Haiku имеется POSIX, то дефайны -D__linux__ или -D__unix__ разрешают многие проблемы, связанные с определением платформы. Однако, стоит заметить, что лучше всего отказаться от их использования и реализовывать поддержку Haiku в исходном коде проекта, если существуют подобные проблемы со сборкой. Вызов системной утилиты finddir с определённым аргументом позволяет получить корректный путь к заголовочным файлам для различных архитектур.

Итак, выполнив команды выше, я скомпилировал исполняемый файл, который прекрасно запускался, а игра отлично работала. Я подумал, что было бы классно подготовить самодостаточный HPKG-пакет с игрой и для этого углубился в интернет на поиски необходимой мне информации. Тогда я не знал ни о каких удобных инструментах для портирования программного обеспечения, вроде HaikuPorter’а, о котором я написал в разделе выше, поэтому для осуществления своей цели я решил схитрить и разобрать какой-нибудь системный пакет, чтобы посмотреть как он устроен внутри и сделать по аналогии.

На просторах интернета я нашёл желаемую информацию, после чего распаковал случайный системный пакет с помощью встроенного в местный файловый менеджер архиватора Expander, нашёл файл .PackageInfo, отредактировал его и, в соответствии со структурой своего приложения, подменил файлы. Затем я просто выполнил команды для сборки HPKG-пакета и его установки в систему:

package create -C AAAA/ aaaa.pkg
pkgman install aaaa.pkg


К сожалению, запуск игры из меню «Applications» не увенчался успехом. Запустив исполняемый файл в терминале, я получил ошибку, говорившую о невозможности найти файлы данных, которые были необходимы для запуска и работы приложения. При этом, если в терминале перейти в директорию пакета приложения, то всё запускалось нормально. Это натолкнуло меня на мысль о том, что при запуске игры из меню нужно делать принудительное изменение директории приложения. Подобное можно сделать либо Shell-скриптом, либо изменением исходников игры. Я выбрал второй вариант и добавил нечто похожее на этот код:

#ifdef __HAIKU__
    // To make it able to start from Deskbar
    chdir(dirname(argv[0]));
#endif


В самое начало стартовой функции main (), что полностью решило данную проблему и пакет получился работоспособным. В комментариях к новости про релиз бета-версии Haiku на сайте Linux.org.ru я скинул ссылку на мой собранный пакет и попросил кого-нибудь направить меня в какие-нибудь активные сообщества пользователей этой операционной системы, после чего лёг спать.

fwv-lgs1iqaf-bm-gdsoft2dwww.png


Порт игры Adamant Armor Affection Adventure, запущенный в операционной системе Haiku.

На утро мне на e-mail написал человек, использующий ник 3dEyes**. Как позже оказалось, за этим именем скрывался Герасим Троеглазов — один из активных разработчиков Haiku и автор порта фреймворка Qt для этой операционной системы. Он показал мне репозиторий HaikuPorts и рассказал как пользоваться утилитой HaikuPorter. Кроме того, он написал рецепт для сборки HPKG-пакета игры Adamant Armor Affection Adventure и добавил её в HaikuDepot.

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

Кроме того, я узнал, что в Haiku нет аппаратного ускорения 3D-графики и тот же OpenGL отрисовывается программно с помощью мощностей CPU. Для тяжёлых графических приложений это, конечно, никуда не годится, но для старых игр этого более чем достаточно. Я даже решил специально проверить пакет игры и установил Haiku на свой старый ноутбук, то есть на реальное железо. На моё удивление, картинка Adamant Armor Affection Adventure рендерилась настолько быстро, что если бы мне не сказали про отсутствие аппаратного ускорения, я бы и не заметил того, что рендеринг осуществляется моим процессором.

Исходный код проекта: https://github.com/EXL/AdamantArmorAffectionAdventure

Ручное создание HPKG-пакетов я отложил до лучших времён и полностью перешёл на использование инструмента HaikuPorter и написание рецептов. Но иногда бывают ситуации, когда требуется ручная пересборка пакета. Например, если HaikuPorter выставил в файле .PackageInfo слишком высокую «ночную» версию Haiku, а пакет нужно протестировать на релизной версии операционной системы. Стоит отметить, что именно благодаря отзывчивости и опыту Герасима я смог разобраться во многих тонкостях создания пакетов для операционной системы Haiku и продолжил свою работу далее.

<< Перейти к содержанию

3. Доработка существующего порта NXEngine (Cave Story)


Я был несказанно удивлён, обнаружив в репозитории HaikuPorts рецепт, который ссылался на мой форк движка NXEngine для игры Cave Story, который я очень давно разбирал в своём блоге. Рецепт и патчи подготовил разработчик по имени Zoltán Mizsei, использующий ник extrowerk и являющийся активным мейнтейнером множества пакетов для Haiku.

Поверхностный анализ, установка пакета и запуск приложения выявил те же проблемы, которые я описывал в предыдущем разделе этой статьи: сохранения игры не работали, настройки тоже не сохранялись, кроме того у пакета не было оригинальной иконки. Я решил исправить эти недочёты и начал работать над патчем, сперва интегрировав все наработки extrowerk’а. Я написал оригинальный Makefile для операционной системы Haiku и поправил запись и сохранение различных пользовательских данных.

cufyxskkttfmgcra7wcq9blp88g.png


Порт игры Cave Story на основе движка NXEngine, запущенный в операционной системе Haiku.

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

#!/bin/bash
if [[ `locale -l` == ru* ]] ;
then
    EXE="`finddir B_SYSTEM_APPS_DIRECTORY`/NXEngine/RUS/Cave Story"
else
    EXE="`finddir B_SYSTEM_APPS_DIRECTORY`/NXEngine/ENG/Cave Story"
fi
"$EXE" $@


Этот скрипт запускается при выборе пункта игры в меню «Applications» и определяет текущую системную локаль. В том случае, если пользователь выбрал русский язык в качестве системного, запустится русская версия игры, а во всех остальных случаях — английская.

А вот с созданием оригинальной иконки для приложения пришлось изрядно повозиться. Дело в том, что в операционной системе Haiku разрешены только векторные иконки специального формата HVIF, которые устанавливаются в качестве атрибутов файловой системы Be File System. В официальной документации существует два больших мануала, посвящённых созданию собственных иконок для приложений: первый мануал описывает стилистику рисовки и дизайн, а второй мануал подробно рассказывает как пользоваться системной программой Icon-O-Matic, предназначенной для создания иконок.

Icon-O-Matic позволяет импортировать простейшие SVG-файлы и экспортировать получившуюся иконку в необходимый для HaikuPorter’а формат, называемый HVIF RDef и представляющий собой тот же HVIF, но преобразованный в текстовый вид. RDef-файлы могут содержать не только изображения, но и дополнительную информацию, например, версию приложения и его описание. Чем-то эти файлы напоминают RES-файлы, используемые в Windows. Следующие команды в рецепте компилируют RDef-файлы и устанавливают получившийся результат в специальные атрибуты:

rc nxengine-launcher.rdef
resattr -o "$appsDir/NXEngine/Cave Story" nxengine-launcher.rsrc

addResourcesToBinaries $sourceDir/build/nxengine-rus.rdef "$appsDir/NXEngine/RUS/Cave Story"


Кроме того, в рецептах определена функция addResourcesToBinaries, позволяющая автоматизировать эту работу. Проблема с программой Icon-O-Matic имеется одна, но очень серьёзная: те SVG-файлы, которые сохраняет популярный векторный редактор Inkscape, либо не открываются, либо импортируются без поддержки некоторых необходимых возможностей, например, градиентов. Поэтому приключенческий квест с конвертированием растровых изображений в векторные через использование различных платных и бесплатных online- и offline-конверторов, а потом открытием получившихся SVG-файлов в программе Icon-O-Matic, я с треском провалил. Позже я решил проблему открытия SVG-файлов и нашёл обходной путь, но об этом я напишу ниже. А пока я решил воспользоваться стандартными возможностями программы Icon-O-Matic и нарисовать иконку самостоятельно. Спустя полчаса работы по усердному копированию пикселей у меня получилось следующее художество:

3dzwpkyex_ccbhffzoqovf9ioue.png


Стандартная программа Icon-O-Matic в операционной системе Haiku.

Да, я использовал векторный редактор для создания изображения в жанре Pixel Art. На мой дилетантский взгляд человека, который слабо разбирается в искусстве, получилось вполне неплохо. Я сохранил эту иконку в нужном формате, подготовил все изменения, обновил рецепт и отправил всё в репозиторий HaikuPorts.

Исходный код проекта: https://github.com/EXL/NXEngine

Получившиеся пакеты я отправил на всякий случай и на фанатский сайт игры Cave Story (Doukutsu Monogatari), администрация которого добавила операционную систему Haiku в раздел загрузок.

<< Перейти к содержанию

4. Портирование игры Gish


Следующим проектом, который я решил перенести на Haiku, стала игра Gish, которую ранее я уже переносил на Android. В репозитории HaikuPorts был рецепт для недоделанной свободной реализации игры под названием Freegish, поэтому я решил добавить туда ещё и оригинальную игру, но без файлов данных, так как они, в отличие от движка, поставляются отдельно и вовсе не бесплатны.

yudmr9o3bxzmrc5a6shyyxjvj_w.jpeg


Порт игры Gish, запущенный в операционной системе Haiku.

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

cmake gish/src/main/cpp/ \
    -DGLES=0 \
    -DANDROID=0 \
    -DSDL2_INCLUDE_DIR=`finddir B_SYSTEM_HEADERS_DIRECTORY` \
    -DCMAKE_C_FLAGS="`sdl2-config --cflags` -D__linux__" \
    -DCMAKE_BUILD_TYPE=Release
cmake --build .


Далее я реализовал возможность запуска игры из меню «Applications» и обеспечил поддержку сохранения пользовательских данных в доступную для записи и предназначенную для этого директорию:

char* getHaikuSettingsPath()
{
    char path[PATH_MAX];
    find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, path, sizeof(path));
    strcat(path, "/Gish/");
    return strdup(path);
}


Функция getHaikuSettingsPath () с помощью функции find_directory () из Haiku API формирует полный путь до необходимой мне директории.

Исходный код проекта: https://github.com/EXL/Gish

Оставалось решить следующий вопрос: каким образом пользователь должен выбирать директорию с оригинальными файлами игры Gish? Проблему можно было попытаться решить с помощью Shell-скриптов и системной утилиты alert, но я решил подойти к этой проблеме более основательно и реализовать удобный GUI-лаунчер, используя Haiku API и фреймворк Interface Kit.

<< Перейти к содержанию

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


Мой проект BeGameLauncher было решено писать на языке C++ старого стандарта 1998 года, используя родные средства операционной системы для создания приложений с графическим интерфейсом пользователя. Так как названия многих программ для Haiku и BeOS начинаются с перефикса «Be», мной было тоже решено выбрать именно такое название для проекта. Начать я решил с ознакомления с фреймворком Interface Kit, который входит в состав Haiku API. Кроме достаточно подробной документации на официальном сайте Haiku, я нашёл два просто отличных курса уроков от DarkWyrm, которые позволяют быстро понять начинающему разработчику как работают те или иные системные классы. Первый курс называется Learning to Program with Haiku и в самом начале затрагивает основы языка программирования C++, что будет очень полезно начинающим программистам. Второй курс называется Programming With Haiku и предназначен для тех, кто уже знаком с C++ и имеет базовые знания этого языка. Оба курса рассказывают о самых различных аспектах Haiku API и поэтому будут очень полезны любому человеку, который хочет начать создавать приложения для этой операционной системы.

Прочитав по диагонали этот отличный материал, я составил общее впечатление о Haiku API и начал обдумывать свои дальнейшие действия. У меня уже имелся небольшой опыт разработки прикладных приложений с использованием фреймворка Qt, который тоже написан на языке программирования C++ и использует объектно-ориентированную парадигму построения программ. Так вот, Haiku API очень сильно на него похож, за исключением отсутствия системы сигналов и слотов, поэтому я буду часто проводить некоторые параллели и сравнения с Qt. Кроме того, стоит отметить распространённое в Haiku API использование принципа Event-driven programming, который позволяет взаимодействовать различным сущностям между собой посредством передачи событий или сообщений. Аналогом класса QEvent здесь является класс BMessage, вокруг которого и построена система взаимодействия объектов. Экземпляр класса BMessage в общем случае получает уникальное число, которое позволяет идентифицировать отправителя и его действие в общем фильтре событий.

Для моего проекта нужно было выбрать подходящие классы Haiku API, которые позволяли бы реализовать задуманную функциональность. Во-первых, для запуска внешнего приложения, нужно было найти аналог класса QProcess или POSIX-функции execve (), которая, к слову, тоже отлично работает в операционной системе Haiku, однако я решил, что использовать родные средства будет предпочтительнее, но на всякий случай оставил возможность запуска приложений и через POSIX-функцию. Класс BRoster, занимающийся межпроцессным взаимодействием, отлично подходил для этой цели. В нём нашёлся подходящий метод Launch (), позволяющий задать путь до исполняемого файла и передать ему аргументы. Поскольку лаунчер должен иметь возможность сохранения некоторых параметров, например, выбранной пользователем директории с файлами данных игры, мне нужен был класс, который занимается всем этим. В Qt такой класс имеет название QSettings, а в Haiku API, как мне подсказал Герасим, имеется уже знакомый мне класс BMessage, который имеет очень полезную особенность. Всё дело в том, что информацию этого класса можно легко сериализовать и, например, сохранить на диск. Это очень удобно и часто используется для записи каких-либо пользовательских данных в программах, поэтому именно этот класс я и выбрал для сохранения настроек в своём проекте реализации лаунчеров. К сожалению, в Haiku API не нашлось аналога класса QDebug, поэтому требуемый мне отладочный вывод в процессе разработки я просто отправлял в stderr, средствами функции fprintf () из стандартного языка программирования C:

// .h
#if __cplusplus >= 201103L
#define BeDebug(...) fprintf(stderr, __VA_ARGS__)
#else
extern void BeDebug(const char *format, ...);
#endif // __cplusplus == 201103L

// .cpp
#if __cplusplus < 201103L
#include 

void
BeDebug(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
}
#endif


Эту функцию я обернул в удобную мне сущность BeDebug (), которая в зависимости от выбранного стандарта языка является либо макросом, либо тоже функцией. Так было сделано из-за того, что C++98 не поддерживает макросы с переменным количеством аргументов.

Ещё во фреймворке Qt имеется полезный класс QMessageBox, через который можно создать модальный диалог с какой-либо информацией, на которую должен обратить внимание пользователь, например, на ошибку или предупреждение. В Haiku API для этих целей имеется класс BAlert, реализация которого несколько отличается от того, что доступно в Qt. Например, объект этого класса обязательно должен быть создан в куче, а не на стеке, поскольку после какого-либо действия пользователя он должен удалить сам себя. Что же касается других классов графического интерфейса, то здесь у меня не возникло абсолютно никаких трудностей и всё необходимое я нашёл без каких-либо проблем.

Теперь мне следовало подумать о простенькой архитектуре проекта. Я решил остановиться на создании статической библиотеки, в которой бы имелось два класса, предназначенных для унаследования от них собственных производных классов. Первый и самый важный класс, BeLauncherBase, отвечает за создание главного окна лаунчера, передачу всех пользовательских параметров и предоставляет возможность добавления собственных GUI-элементов. Второй класс, BeAboutWindow, просто отвечает за открытие диалога «О программе…» с информацией, которая показывается в отдельном окне. Таким образом, программисту для создания своего лаунчера, например, для игры Gish, требуется сделать два простых действия:

class GishAboutWindow : public BeAboutWindow
{
    ...
};

class GishLauncher : public BeLauncherBase
{
    ...
};

int
main(void)
{
    BeApp *beApp = new BeApp(SIGNATURE);
    GishLauncher *gishLauncher = new GishLauncher(BeUtils::GetPathToHomeDir());
    beApp->SetMainWindow(gishLauncher);
    beApp->Run();
    delete beApp;
    beApp = NULL;
    return 0;
}

Во-первых, создать подходящую стартовую функцию main (), а во-вторых просто унаследоваться от двух вышеперечисленных классов и реализовать в них необходимые методы. После этого компилируем полученный C++-файл с линковкой к моей статической библиотеке и наш лаунчер для игры Gish готов.

k1hyiwbvnz6gnxaar4p7k2tnsso.png


Диалог «О программе…» в лаунчере порта игры Gish.

Далее я задумался о том, каким образом мне передавать из своего лаунчера параметры в сам движок или в исполняемый файл игры. Я увидел только два пути решения этой проблемы. Первый путь заключался в изменении переменных окружения. На практике лаунчер после нажатия на кнопку «Run» просто помещает все параметры в переменные окружения посредством вызовов функции setenv (), а движок игры потом читает эти параметры с помощью функции getenv (), что выглядит достаточно просто. Единственная проблема, которая могла здесь возникнуть, находилась в классе BRoster и его методе Launch (): я не знал, унаследует ли запускаемое с помощью этого класса приложение все те переменные окружения, которые были выставлены в лаунчере. После небольшого эксперимента наследование переменных окружения подтвердилось и этот способ я полностью реализовал в своём проекте. Второй путь решения проблемы заключался в задании специальных параметров командной строки. На практике лаунчер просто складывал все настройки в соответствующие аргументы и вызывал с ними исполняемый файл приложения. А вот движок игры уже должен был самостоятельно их обработать, что создавало некоторые сложности. Например, если игра не предполагала возможность задания пути до игровых файлов через параметры командной строки, то нужно было модифицировать парсер аргументов в самом движке. Несмотря на эти проблемы, я реализовал и этот способ взаимодействия и в итоге получил отличную возможность совмещать всё вместе. Это позволило мне создавать в некоторых лаунчерах строку задания пользовательских аргументов.

Когда всё было спроектировано, я решил выбрать сборочную систему для своего проекта. Рассматривалось только два варианта: Makefile «на стероидах» и CMake. В первом случае, разработчики операционной системы Haiku подготовили удобный пакет makefile-engine, в котором собрали все необходимые возможности, с которыми столкнулся бы разработчик, начав писать приложение на Haiku API, например, автоматическую генерацию переводов и компиляцию ресурсов приложения. Но я не из тех, кто ищет лёгкие пути, поэтому я выбрал CMake и перенёс в него некоторые наработки из пакета makefile-engine. В итоге получившийся монструозный сборочный скрипт вы можете посмотреть в репозитории проекта, ссылку на который я оставлю ниже.

jyfwwhdhgxzofr_3cczuxxvmsig.png


Скриншот лаунчера порта игры Gish для Haiku.

Хотелось бы написать пару слов о локализации приложений. Во фреймворке Qt для этого имеется удобная функция-обёртка tr (), две вспомогательные утилиты lrelease и lupdate, которые занимаются генерацией файлов перевода. В комплекте с фреймворком доступна даже специальная программа Qt Linguist с удобным графическим интерфейсом пользователя, предназначенная для переводчиков. В Haiku API инструменты для локализации приложений менее удобные и более архаичные. Строки, которые нужно перевести, предлагается оборачивать в специальный макрос B_TRANSLATE (), а в исходный файл добавлять определение B_TRANSLATION_CONTEXT, которое отделяет одну группу переводимых строк от другой. После этого требуется выполнить очень странную вещь: натравить препроцессор компилятора с флагом -DB_COLLECTING_CATKEYS на абсолютно все исходные файлы проекта, сделать какую-то магию с помощью утилиты grep и в итоге получить PRE-файл громадного размера. Именно с этим файлом и будет работать утилита collectcatkeys, которая уже создаст человекочитаемые и удобные для редактирования переводчику CATKEYS-файлы. После локализации строк необходимо воспользоваться утилитой linkcatkeys, которая добавляет переводы в ресурсы исполняемого файла. Таким образом, при выборе определённого системного языка приложение отображает переведённые строки. Странно, но в документации Haiku API о локализации приложений содержится очень мало информации. Однако, на официальном сайте я нашёл отличную статью Localizing an application, в которой подробно рассмотрены многие аспекты переводов приложений для этой операционной системы. Как я понял, в оригинальном BeOS не было фреймворка Locale Kit и он был добавлен уже только в Haiku.

Следующим моим шагом стал выбор среды для разработки приложений на языке программирования C++. Благодаря тому, что на Haiku был портирован фреймворк Qt, в репозитории HaikuPorts доступны такие IDE, как Qt Creator и KDevelop. Кроме того, имеется порт JVM, что позволяет использовать IDE, написанные на языке программирования Java, например, NetBeans или IntelliJ IDEA. Я остановил свой выбор на среде разработки Qt Creator, тем более в её последних версиях имеется качественный разбор кода с помощью парсера LibClang, который работает на порядок точнее и быстрее стандартного парсера.

skjgheksjieuiyylk7uhe1_dk6e.png


Интегрированная среда разработки Qt Creator, запущенная в операционной системе Haiku.

В плане общеизвестных и кроссплатформенных IDE в Haiku всё хорошо. Но что насчёт эксклюзивных решений? Я не могу не упомянуть очень интересный проект, автором которого является DarkWyrm и который в настоящее время поддерживает Adam Fowler, он называется Paladin. Эта программа превращает доступный в дистрибутиве операционной системы продвинутый текстовый редактор Pe практически в настоящую IDE.

xpadjpfvl4sfridhlc4wob9s3a0.png


Интегрированная среда разработки Paladin для Haiku, установленная из репозитория HaikuPorts.

С помощью встроенного в оконную систему Haiku тайлинга можно прикрепить окно Paladin сбоку редактора Pe и добавить терминал. Ещё в репозитории HaikuPorts имеется

© Habrahabr.ru