Здравствуйте!
Меня зовут Андрей Дедюхин, и в этой статье я хотел бы рассказать о своей собственной разработке — графическом фреймворке “Арго”. По специальности я бэкенд-разработчик ПО, но в моей карьере были и другие ниши, связанные с разработкой и управлением проектами, и я обладаю широким кругозором в разных областях разработки ПО.
Немного предыстории. В 2011 году наша компания выполняла заказ для канадской компании RIM (Research-In-Motion, ныне Blackberry). Необходимо было разработать некий пользовательский интерфейс с использованием фреймворка TAT Cascades. На тот момент я с ним не был знаком, но в течение месяца или около того разобрался. Меня поразило то, как насыщенный пользовательский интерфейс создается обычными XML-описаниями. Cascades действительно поддерживал множество нюансов интерфейса, позволял разработать интерфейс без логической части на каком-либо языке программирования, и, собственно, разработка интерфейса велась весьма быстро. Однако у TAT были и серьезные архитектурные недостатки. RIM купил этот фреймворк, надеясь, что сможет его довести до ума, но время показало тщетность этих планов. Компания свернула работы с этим фреймворком, но сохранила правообладание. В общем, обычная ситуация: что в RIM попало, то пропало. После сворачивания разработок я больше об этом фреймворке не слышал.
Поскольку уже в 2011 году было понятно, что RIM этот фреймворк не отдаст, возникла идея написать собственный фреймворк. Опираясь на свой опыт, а к этому времени он у меня уже был немаленький, я решил, что “не боги горшки обжигают”, и начал делать первые прототипы. За следующие несколько лет, занимясь этим проектом вне ребочего времени, сделал три прототипа, на которых устранялись архитектурные недостатки и шлифовались отдельные решения. По результатам этой работы выкристаллизовалась архитектура и многие решения, которые успешно применяются в “Арго” до сих пор, хотя отдельные шероховатости все еще шлифуются.
В 2016 году было принято решение о переходе к четвертому прототипу. Эта версия начиналась как прототип, но постепенно перешла уже к полноценной разработке фреймворка. Разработка шла медленно, по тем же причинам, но успешно, фреймворк начал принимать нынешние очертания. К 2022 году он уже имел большинство важных функционалов. Компания, в которой я работал с 2004 года, и которая после ребрендингов стала Harman Russia, покинула российский рынок в 2022 году (с немалым, надо сказать, сожалением). После этого я и занялся фреймворком всерьез.
Само название “Арго” было придумано спонтанно, надо было как-то назвать. Вот и вспомнился древнегреческий миф о долгом и трудном пути за шкурой золотого барана.
Теперь настало время присмотреться к этому “золотому барану”. 🙂
Основные принципы и возможности фреймворка
Фреймворк задумывался как абсолютно платформонезависимая разработка, и, в большей части, это получилось. Основой его стало операционное ядро, которое выполняет все операции по обслуживанию пользовательского интерфейса и максимально изолировано от операционной системы и стандартных библиотек. Ядро не содержит вызовов стандартных функций и пользуется только библиотекой интеграционного уровня, в которой находятся нужные функции-обертки. Эта идея, конечно, не нова. Но интеграционный уровень реализует только необходимый минимум, причем большинство оберток использует POSIX-функции, и их реализация практически не меняется на POSIX-совместимых системах. Интеграционный уровень реализует только около 120 наиболее употребительных функций и только десяток функций, специфичных для конкретной операционной системы, поэтому интеграционный уровень очень “тонкий”. Ядро и связанные с ним библиотеки не используют стандартных заголовочных файлов, там даже функцию printf() вызвать не получится.
Ядро и все библиотеки полностью написаны на языке “Си”. Выбор языка также определялся мультиплатформенностью и независимостью от стандарта языка. “Си” он и в африке “Си”, хотя интеграционный уровень не полностью соответствует стандарту C99. Позднее было сделано много дополнений в POSIX стандарт, это не совместимо со строгим следованием данному стандарту C99 (хотя такая реализация вполне возможна). Тем не менее, есть вещь, которая существенно облегчает жизнь и вес кода фреймворка: его реализация напоминает Component Object Model (COM), но, разумеется, таковой не является. Это подразумевает использование объектов модулей, которые реализованы в виде псевдо-классов, структур на “Си” и функций-методов данного псевдо-класса. Использование “Си” имеет и ряд других преимуществ, например, жесткий внутренний контроль за использованием оперативной памяти без инструментальных средств — если в процессе работы фреймворка утечет хотя бы один байт, это будет сразу же известно. Функциональность отключаемая, но крайне полезная. В сочетании с предыдущим пунктом, ядро и библиотеки практически могут быть собраны любым компилятором (даже C++ компилятором). Ни ядро, ни библиотеки фреймворка не используют Open Source решения, весь код оригинальный. Конечно, я пользовался открытым кодом, но только чтобы разобраться в сложных вещах, а во фреймворке их немало.
К основным возможностям фреймворка относится, в первую очередь, полноценный уровень приложений. Он позволяет предустанавливать системные приложения, устанавливать и удалять пользовательские приложения. Уровень приложений отвечает за их запуск (автоматически или по запросу пользователя), взаимодействие приложения с подсистемами фреймворка, выгрузку или перевод приложения в фоновое состояние. Графические приложения реализуются, используя раздельные описания пользовательского интерфейса (UI) на XML и бизнес-логики приложения, написанной на C/C++ или Python. Есть еще планы на добавление Java-описаний. Про приложения я еще расскажу немного позже.
Еще одной из основных возможностей является поддержка нескольких дисплеев на уровне ядра фреймворка, при этом требуется поддержка дисплеев и на интеграционном уровне. В терминах фреймворка дисплеем может считаться любое устройство ввода/вывода: физический дисплей, сенсорная панель (touchpad), клавиатура. Это позволяет легко настраивать фреймворк под различные конфигурации оборудования и обрабатывать события от него унифицированным образом. Каждый дисплей содержит один или несколько виртуальных суб-дисплеев. Виртуальными — так как не содержат физической памяти для отрисовки изображений и определяют лишь границы, в которых будет отрисовываться окно приложения, привязанное к этому суб-дисплею. Количество суб-дисплеев также не ограничивается, но последовательность объявлений определяет приоритет суб-дисплея: самый первый определенный суб-дисплей имеет минимальный приоритет, все последующие идут с увеличением приоритета. Суб-дисплеи могут занимать все пространство дисплея или только часть его, перекрываться друг с другом. Основное назначение суб-дисплеев — определение приоритетов окон приложений.
Фреймворк обладает мощным механизмом конфигурации для различных задач. Помимо конфигурации интеграционного уровня, фреймворк предоставляет несколько дополнительных механизмов конфигурирования.
Первый механизм — конфигурирование начальных стартовых параметров. Их можно задать через специальный файл, переменные окружения или опции командной строки при запуске. Эти механизмы имеют свои приоритеты: параметры, установленные в конфигурационном файле, могут быть переопределены переменными окружения; переменные окружения, в свою очередь, могут быть переопределены параметрами командной строки.
Второй механизм — конфигурационное XML-описание, являющееся основным. Какое именно конфигурационное описание будет использоваться, определяется стартовыми параметрами. Конфигурационное XML-описание задает все параметры системы:
-
дисплеи фреймворка, их размеры и параметры
-
виртуальные суб-дисплеи
-
графические и прочие ресурсы системы (точнее, определяет файлы, в которых эти ресурсы описаны)
-
системные базы данных для хранения параметров/переменных
-
список предустановленных приложений и их параметры
-
пути к файловым системам, доступным приложениям
-
стили окон приложений, темы, доступные языки системы
Третий механизм — переопределение некоторых глубинных конфигурационных параметров системы (sysconfig) непосредственно в конфигурационном XML-описании.
Конфигурационные параметры включают значения по умолчанию, которые используются при старте системы. Они определяют размеры внутренних буферов, глубину очередей для передачи событий и различные внутренние параметры, позволяя производить тонкую настройку системы. Значения по умолчанию могут быть переопределены в системном XML-описании.
Данная статья носит исключительно обзорный характер, гораздо больше полезной информации можно получить по линкам ниже.
В открытом доступе есть только SDK-версия фреймворка, и материалы на Википедии. SDK размещен на российской платформе GitFlic (российский аналог GitHub/GitLab), доступ на чтение и клонирование открыт для всех желающих. SDK доступен только для Linux 64-бит, 32-х битовая версия тоже есть, но я её не выкладываю. Я использую Ubuntu 20/22, более новые версии тоже вряд ли вызовут проблемы. Ограничения связаны только с интеграционным уровнем, например, работоспособного варианта под Windows пока нет.
Архитектура фреймворка
На самом верхнем архитектурном уровне фреймворк состоит из следующих компонентов: библиотека ядра фреймворка, модуль системы (там по сути только функция main()), библиотеки интеграционного уровня и набор вспомогательных библиотек ядра (библиотека для формирования бинарных файлов и их загрузки, библиотеки приложений, библиотека для отрисовки графики, библиотека для управления оперативной памятью и некоторые другие).
Библиотека ядра фреймворка состоит из двух частей: собственно операционное ядро системы и SDK-библиотека. Операционное ядро выполняет все операции внутри фреймворка, за исключением парсинга XML-описаний. Большая часть обработки сосредоточена непосредственно в ядре, но некоторые процедуры вынесены в сопутствующие библиотеки, перечисленные выше. SDK-библиотека предназначена для парсинга XML-описаний, проверки корректности описаний и формирования внутреннего бинарного описания. Бинарное описание используется ядром и может быть выгружено в бинарный файл, используя утилиты фреймворка. Такой бинарный файл далее может использоваться вместо XML-описаний. SDK-версия фреймворка содержит обе эти части и может использовать XML-описания на этапе разработки. Бинарные описания обеспечивают большой выигрыш по времени загрузки по сравнению с XML (загрузка в десятки раз быстрее). Исходный код компонентов ядра и его библиотек недоступен в SDK, а представлен в виде набора скомпилированных библиотек.
Как уже упоминалось, модуль системы содержит только функцию main() и выполняет две основные и простые операции: создает экземпляр (объект) интеграционного уровня и производит вызов функции sysmain() ядра фреймворка. Реализация модуля системы зависит от ОС (например, в Windows должна быть функция winmain()), поэтому код системного модуля открыт в SDK. По мере дальнейшего развития фреймворка модуль может быть усложнен, допустим, вся система запускается под root-пользователем для взаимодействия с драйверами, а ядро фреймворка и его приложения — под другим пользователем, в отдельном процессе. Для SDK root-пользователь для запуска не требуется.
Интеграционный уровень содержит набор библиотек: основную (common) библиотеку для реализации необходимого фреймворку API, за исключением механизмов ввода/вывода; библиотеку для реализации ввода/вывода — обслуживание дисплея и получение событий пользовательского ввода; библиотеку для логирования (logger); отдельные библиотеки для работы с сокетами и терминальным вводом/выводом — последние две библиотеки не используются фреймворком и нужны только для SDK-утилит фреймворка. Весь исходный код интеграционного уровня открыт и может модифицироваться разработчиком в соответствии с его запросами. Однако модифицировать API не позволяется, только его реализацию. Также надо учитывать, что модификации могут вызвать сбой ядра фреймворка.
Очень кратко сделаю обзор модулей ядра фреймворка:
-
Модуль собственно ядра: отвечает за запуск и выгрузку модулей ядра в определенном порядке и в соответствии с базовыми параметрами и конфигурационным описанием. По базовым параметрам фреймворк определяет, где взять конфигурационное описание в виде XML или бинарного файла, получает бинарное описание системы, используя SDK-библиотеку или библиотеку загрузки бинарных файлов, и производит загрузку системных модулей в соответствии с этим описанием. Когда фреймворк выгружается, ядро производит выгрузку модулей в заданном порядке.
-
Менеджер приложений: предназначен для реализации всех операций, связанных с запуском приложений, переводом приложений в фоновое состояние и обратно, закрытием приложений. Менеджер отвечает за взаимодействие приложения с графической подсистемой ядра и с другими модулями и приложениями. Приложение рассматривается менеджером как некоторая абстрактная сущность — он знает, как его запустить, может получать от него события и отправлять ему сообщения, у него есть информация, активно приложение или нет. Информация об установленных приложениях запрашивается у менеджера установки.
-
Менеджер установки: отвечает за сохранение параметров приложения при их установке и удалением установочной записи, если приложение удалено. Приложения разделяются на предустановленные или системные (т.е. прописанные в системной конфигурации и размещенные на файловой системе по определенным правилам — Core applications) и на пользовательские (установленные по запросу от пользователя — User applications). При установке нового приложения менеджер добавляет информацию в специальный файл, и она будет доступна после перезагрузки системы. По запросу от менеджера приложений установочная информация будет предоставлена для запуска приложения.
-
Сокет приложений: отвечает за взаимодействие приложения с менеджером приложений. Когда менеджер приложений запускает новое приложение, он создает экземпляр сокета приложения. Сокет приложения отвечает за передачу событий от фреймворка к приложению (Downlink messages) и от приложения к фреймворку (Uplink messages). Сокет отвечает за трансляцию событий в обоих направлениях, т.е. выполняет некоторое преобразование изначального события — добавление или удаление служебной информации в зависимости от направления передачи. Также сокет создает экземпляр библиотеки приложения, которая непосредственно взаимодействует с приложением и предоставляет ему API для “общения” с фреймворком.
-
Плеер: корневой модуль графической системы, предназначенный для отрисовки окон приложений, обработки пользовательского ввода (обработка событий с сенсорного экрана или событий от клавиатуры, например). Для каждого дисплея создается свой экземпляр плеера, и он обслуживает все операции с данным дисплеем. Это крайне сложный модуль, отвечающий за все графические операции с окнами приложений и графическими виджетами. Архитектура плеера позволяет легко добавлять новые встроенные виджеты в виде новых суб-модулей плеера. Встроенными они называются, потому что фреймворк знает, как обрабатывать поведение таких виджетов. Есть и пользовательские виджеты, управление их поведением лежит полностью на разработчике. Для отрисовки графических элементов плеер запрашивает графические ресурсы у менеджера ресурсов. Плеер плотно взаимодействует с менеджером приложений: создание и удаление окон, передача сообщений в приложение и получение запросов от приложений. Окна приложений, графические виджеты создаются с использованием соответствующих XML-описаний или бинарных файлов. Плеер выполняет отрисовку графических элементов в специальном фреймбуфере. Как только все операции с этим буфером завершены, плеер запускает механизм рендеринга дисплея — обновление содержимого экрана из этого фреймбуфера.
-
Менеджер ресурсов: предназначен для управления различными типами графических, текстовых и других ресурсов системы и отдельных приложений. В общем случае фреймворк использует идентификаторы ресурсов, прописанные в специальных конфигурационных XML-файлах. Менеджер ресурсов обеспечивает загрузку ресурсных файлов, конвертацию графических форматов во внутренний универсальный формат, управление загрузкой/выгрузкой ресурсов, оптимизацию расхода оперативной памяти, выгрузку ресурсов приложения при выгрузке приложения и ряд других операций. Ресурсы находятся в двух доменах: системные ресурсы и ресурсы приложений. Системные ресурсы могут использоваться явно или неявно любыми приложениями. Напрямую системными ресурсами без ограничений могут пользоваться только системные приложения. Приложения в пользовательском режиме тоже могут использовать системные ресурсы, если данные ресурсы прописаны в элементах темы (подробнее в следующем пункте). Менеджер позволяет также создавать динамические ресурсы по запросу от приложения, например, для отображения пользовательских фотографий или пользовательских иконок. Помимо основной функции, менеджер ресурсов отвечает за смену языка системы. Ресурсы приложений могут использоваться только конкретным приложением, точнее, всеми экземплярами приложения.
-
Менеджер данных: предназначен для сохранения строковых, целочисленных, перечислимых данных, а также массивов для различных целей: обмена данными между приложениями, сохранения данных при перезагрузке системы, оповещения о событиях в системе и тому подобное. Другие типы данных (с плавающей точкой, например) пока не поддерживаются. По аналогии с менеджером ресурсов есть два домена для хранения данных: системные и данные приложений. Фреймворк создает две дополнительные системные базы данных — автоматическую и общую, это делается независимо от пожеланий разработчика системы. Автоматическая база данных предназначена для хранения параметров системы, таких как количество дисплеев, язык системы и других внутренних параметров. Переменные в этой базе имеют предопределенные имена и доступны только для чтения любыми приложениями. Разработчик системы может только сконфигурировать этот механизм, чтобы сохранять данные между перезагрузками системы или отключить сохранение. Общая база данных позволяет читать переменные любому приложению, но создавать и записывать переменные могут только системные приложения. Эти переменные, в первую очередь, предназначены для обмена данными между приложениями и посылки уведомлений. Общая база данных никогда их не сохраняет между перезагрузками системы. Системные базы данных определяются в конфигурационном XML-файле системы и предназначены для общей информации, нужной системным приложениям. Пользовательские приложения к ним доступа не имеют. Системные приложения могут записывать, читать или включать для себя уведомления из этих переменных. Такие переменные могут сохраняться при перезагрузке системы. Базы данных приложений доступны только приложению и могут быть использованы для обмена данными для нескольких экземпляров приложения. Также могут сохраняться данные, которые нужны после перезагрузки приложения.
-
Менеджер файловой системы: управляет доступом фреймворка и приложений к определенным директориям на нативной файловой системе. Каждая такая директория представляется как виртуальная файловая система и отображается в пользовательском интерфейсе. Список таких файловых систем определяется в конфигурационном XML-файле. Менеджер собирает полную информацию о файлах внутри таких директорий и предоставляет её по запросу системных приложений. Пользовательские приложения такую информацию получить не смогут. Менеджер создает две дополнительные виртуальные файловые системы: папку для хранения системной информации (var), которая используется исключительно фреймворком для хранения внутренней информации, и домашнюю папку (home) для размещения файлов приложений. Все графические приложения могут получить информацию о конкретных файлах через системное графическое приложение-сервис FileView. Создание и управление файловыми системами выполняется через системное XML-описание, используя системные конфигурационные параметры. Менеджер может получать и обрабатывать уведомления о подключении новых реальных файловых систем, например, USB-диска. При получении уведомления менеджер зарегистрирует такую файловую систему, и она будет доступна приложениям. Дополнительно он обеспечивает уведомление для подписанных системных приложений о том, что файловая система была добавлена или удалена.
-
Менеджер событий: отвечает за своевременную доставку событий от пользователя или приложений в модули ядра. Фреймворк является системой, которая только реагирует на подобные события, и сам никаких событий не генерирует. Модуль весьма прост, но, в целом, механизмы внутреннего мессенджинга не так тривиальны. Все модули-менеджеры объединены в единую сеть, которую и поддерживает менеджер событий. Модуль-передатчик создает тело сообщения и отсылает его по адресу/идентификатору того модуля, куда сообщение передается. Адресация, выбор, пересылка и доставка ответа полностью лежат на менеджере событий. Сообщения могут быть синхронными, то есть происходит прямой вызов интерфейсов вызванного модуля, или асинхронными, когда событие попадает в очередь на обработку, и вызывающий модуль не блокируется. В последнем случае модуль-передатчик сам должен позаботиться о том, чтобы получить ответ, если ему это необходимо. Для приложений это обеспечивается библиотекой приложений.
-
Менеджер тем: как и во многих современных графических системах, во фреймворке есть механизм изменения графического представления с использованием механизма изменения темы. Тема включает в себя разнообразные параметры: общая цветовая гамма (светлая, темная, цветовая), общие ресурсы (управляющие элементы), элементы стиля окон и тому подобное. Элементы темы имеют фиксированные текстовые идентификаторы, имена, которые могут использоваться как идентификаторы ресурсов (например, кнопки по умолчанию), цвета конкретного элемента (например, цвета нарисованного виджета) и в других случаях. Основной список идентификаторов сам по себе фиксирован и не может быть изменен. При необходимости есть возможность добавления пользовательского элемента в рамках системы. Конкретные значения элементов темы определяются в рамках системного конфигурационного файла. Механизм позволяет унифицировать представление приложений, то есть приложения будут отображаться унифицированно, без знания конкретных ресурсов системы. Это особенно полезно разработчикам сторонних приложений, которым, пользуясь фиксированным набором элементов, не нужно знать о ресурсах конкретной системы, о которых сторонний разработчик и знать не может. Однако у разработчика приложения всегда остается возможность использования ресурсов приложения для создания своего уникального интерфейса.
-
Менеджер системных сообщений: предназначен для получения и обработки сообщений о каких-либо проблемах, связанных с запуском и последующей работой приложений, а также об аварийной выгрузке приложения. Менеджер классифицирует пришедшее сообщение и выполняет соответствующие операции. Во всех случаях желательно, чтобы пользователь увидел сообщение о проблеме в пользовательском интерфейсе и сделал свой выбор, что делать дальше. Вывести сообщение об ошибке фреймворк самостоятельно не может (только в собственный лог), поэтому требуется специальное графическое приложение-сервис, которое и выведет сообщение об ошибке на основании данных, предоставленных менеджером, и обеспечит варианты выбора. При старте системы такой сервис регистрируется в менеджере сообщений и будет получать от него сообщения о возникающих ошибках. В качестве дополнения сервис может просто получать сообщения от приложений для вывода некоторой информации о состоянии приложения и выводить информацию непосредственно от него, минуя менеджер сообщений, но с максимальным приоритетом (пока этого нет).
В перспективе планируется добавить менеджер шрифтов, что обеспечит приложения всеми возможностями TrueType-шрифтов. Они уже применены во фреймворке, но используются уже в растеризованном формате (все нативные шрифты фреймворка растровые). Менеджер должен обеспечить динамическую подгрузку TTF-шрифтов и растеризацию в процессе работы. Также планируется сделать обработчик жестов, пока непонятно его место в архитектуре, но, скорее всего, это будет преобработчик сообщений к плееру, и он будет индивидуален для каждого дисплея. Для этого потребуется реализовать механизм мультитач, его затруднительно использовать в симуляторе (SDK), но сам механизм сделать можно.
Приложения фреймворка
Сам по себе фреймворк не содержит встроенных приложений и, по сути, без них бесполезен. Всю полезную работу по созданию качественного графического интерфейса выполняют приложения, используя API (Application Program Interface) фреймворка.
Приложения фреймворка могут классифицироваться по разным признакам: системные или пользовательские, оконные или фоновые, по режиму запуска — статические, динамические или запущенные в отдельном процессе. Ниже будут рассмотрены все эти варианты.
Фреймворк имеет раздельные механизмы описания для пользовательского интерфейса и логической части приложения. Пользовательский интерфейс (окна приложения) описывается в конфигурационных XML-файлах, в то время как логическая часть пишется на C/C++/Python.
Важнейшей характеристикой приложения является его статус — системное или пользовательское.
Системное приложение обладает следующими свойствами:
-
Предустанавливается и не удаляется из системы. Предустановка выполняется в конфигурационном описании системы с размещением файлов приложения по заданным правилам.
-
Имеет расширенный набор привилегий для взаимодействия с системным API, получения системной информации, запуска/закрытия других приложений, работы с файловой системой и системными уведомлениями. Системные приложения могут автоматически запускаться системой, в отличие от пользовательских. Пользовательские приложения тоже могут иметь частичные привилегии, например, запускаться только в единственном экземпляре (singleton).
-
Системное приложение может создавать несколько окон в различных парах дисплей/суб-дисплей. Пользовательское приложение может создавать окна только в дисплее/суб-дисплее по умолчанию.
Пользовательские приложения могут быть установлены и удалены в любой момент работы фреймворка, сохраняясь при перезагрузке.
Приложения могут быть фоновыми (системные приложения без графического интерфейса) или оконными. Пользовательские приложения не могут быть фоновыми, они обязаны иметь хотя бы одно окно — это требование безопасности к сторонним приложениям.
Приложения могут запускаться в трех режимах:
-
Статический: логическая часть статически слинкована с исполняемым модулем фреймворка. Возможно только при сборке фреймворка, и неприемлемо для пользовательских приложений. Однако при отладке приложения в SDK так сделать можно.
-
Динамический: приложение в динамическом режиме представляет собой динамическую загружаемую библиотеку (DLL — Dynamic Load Library). При старте приложения библиотека подгружается, и далее все работает, как и в случае статической библиотеки. Пользовательские приложения могут быть динамическими, но это временно и только для SDK.
-
В отдельном процессе (удаленный режим): удаленный режим подразумевает запуск приложения в другом процессе. При этом пользовательский интерфейс по-прежнему обрабатывается фреймворком, в то время как логическая часть находится в другом процессе. Приложение при этом строится как исполняемый бинарный файл. Обмен информацией между приложением и фреймворком производится с использованием некоторых IPC (Inter Process Communication) протоколов, в частности, SDK может использовать TCP/UDP IP соединения для управляющей информации и разделяемую память для передачи больших объемов данных. В удаленном режиме может работать любое приложение, если время обмена данными с фреймворком не имеет существенного значения.
Приложения на языке Python могут работать только в удаленном режиме. Статический и динамический варианты работать не будут. Связано это с механизмами Python C-API и необходимостью дополнительных операций линковки в таких вариантах. Теоретически, может быть, это и возможно, но практически реализовывать это я не стану. Однако ничего не мешает сделать приложения на Python системными, как и любое другое.
Использование статического или динамического режима запуска приложения полезно для максимального быстродействия. Но при этом приложение работает в одном адресном пространстве с фреймворком. В случае сбоя приложения фреймворк не “крэшится” вслед за ним, как можно было бы ожидать. Но тут есть некоторые нерешенные проблемы с механизмом обработки исключений.
Привилегии приложений предназначены, в первую очередь, для разграничения возможностей системных и пользовательских приложений при управлении системой. Пользовательские приложения не должны иметь возможность вмешиваться в системные процессы, поэтому соответствующие привилегии им недоступны. Системное приложение должно быть предустановлено, иначе получить необходимые привилегии невозможно. Однако, как упоминалось выше, некоторые привилегии все же доступны пользовательским приложениям.
Привилегии представляют собой битовые флаги, разделенные на три диапазона:
-
Привилегии пользовательских приложений: биты 0 — 7.
-
Привилегии системных приложений: биты 8 — 23 (также доступен диапазон 0 — 7).
-
Диапазон отладочных приложений: биты 24 — 31 (все остальные тоже доступны).
В SDK диапазон отладочных приложений недоступен, но позднее такое ограничение может быть снято.
Привилегии устанавливаются либо в конфигурационном файле системы при объявлении системного приложения, либо в установочном файле пользовательского приложения. Можно задавать любое значение, но диапазоны все равно будут соблюдены. В дальнейшем разбивка битов по диапазонам, возможно, изменится, поскольку пока много резервных битов, но потом диапазоны будут уточнены.
Начальный каркас приложения может быть легко создан с помощью скрипта appmake.sh, о нем подробно рассказано на википедии.
Программный интерфейс приложений
Приложения фреймворка должны взаимодействовать с ним от момента запуска до выгрузки. За этот процесс отвечает программный интерфейс приложений (API). API включает в себя:
-
Корневой интерфейс: функции непосредственно библиотеки приложений, являющиеся основным интерфейсом.
-
Функции библиотек поддержки (support libraries): наиболее употребительный интерфейс, представляющий собой удобные обертки для корневого интерфейса.
-
Функции интеграционного уровня: для взаимодействия с операционной системой (необязательно, но предпочтительно).
-
Специальные функции дополнений (supplementary): их использование возможно и, возможно, даже желательно.
Важно внести ясность: приложения фреймворка НЕ ОБЯЗАНЫ следовать правилам, применным к коду ядра. Они могут свободно использовать сторонние функции, библиотеки и заголовочные файлы по усмотрению разработчика. Разработчики фреймворка не несут ответственности за разработку приложений, за исключением тех, что опубликованы в SDK. Использование сторонних API на уровне приложений не запрещено.
Корневой интерфейс — это набор функций библиотеки приложений, представляющий собой самый короткий, но и самый сложный путь к интерфейсу фреймворка. Функции объявлены в заголовочном файле argo-sdk/target/include/frm_app_if.h в SDK. Их немного, но, как правило, они выполняют множество операций в зависимости от переданных параметров. Для большинства функций требуется корректное заполнение структур данных для выполнения соответствующего запроса. Такой подход обусловлен тем, что каждая функция использует собственное, индивидуальное событие фреймворка, а их количество ограничено. Лимит велик, но запас карман не тянет. Заполнение таких структур является относительно формальной задачей, но приводит к значительному усложнению кода приложений из-за многоуровневости структур. Разумеется, некоторые функции часто используются напрямую, без обёрток, но это скорее исключение. Необходимые обёртывающие функции добавляются в библиотеках поддержки (support libraries).
Библиотеки поддержки предоставляют более удобный программный интерфейс, чем корневой. Они реализуют необходимые функции-обертки для вызова корневого интерфейса. Реализация функций-оберток открыта — исходный код находится в папке support в SDK и в рабочем пространстве. Использование и модификация оберток — ваша полная прерогатива. Делайте с ними что хотите, но и претензий не предъявляйте :). Разработки других библиотек-оберток будут приветствоваться, найденные ошибки — исправляться.
Для приложений на языке Python необходима динамическая библиотека (DLL), её код расположен в папке support/libapi/dll/. По сути, функции библиотеки — это тоже обертки для функций корневого интерфейса и библиотек поддержки; они не несут собственной логики. Это лишь интерфейс взаимодействия с приложениями на Python. Без этой библиотеки взаимодействие приложений на Python с фреймворком было бы невозможно. Это связано с механизмом вызова функций “Си” из Python, который возможен только с использованием DLL. Есть и другие библиотеки, связанные с API Python. Приложения используют модули на Python, расположенные в argo-sdk/target/python/, для доступа к этим API. Стоит отметить, что приложения на Python — это относительно новое дополнение к фреймворку, и на данный момент доступен не весь API фреймворка.
Использование функций интеграционного уровня в приложениях является сугубо опциональным. Их не так много, чтобы закрыть все потребности разработки. Функции объявлены в файле argo-sdk/target/include/frm_integration_if.h. Они позволяют безопасно взаимодействовать с операционной системой, поскольку адаптированы под ОС, но их набор явно недостаточен для всех запросов. Интеграционный уровень обслуживает фреймворк, но не обязан обслуживать приложения. При необходимости можно расширить интеграционные библиотеки и заголовочные файлы, но менять существующий интерфейс нельзя. Также интеграционный уровень содержит некоторый набор библиотек, которые могут быть полезны, например, библиотека логирования. Все приложения в той или иной мере используют макросы для логирования; реализация логирования осуществляется именно в этой библиотеке (все макросы объявлены в файле argo-sdk/target/include/frm_comdef.h).
Приложения фреймворка могут использовать и некоторые дополнительные библиотеки, API которых доступен. В частности, библиотеку libfrmsuppl.so (supplementary library). Код этой библиотеки закрыт, она широко используется ядром и утилитами фреймворка. Библиотека реализует такие компоненты, как списки, древовидные списки, очереди и набор вспомогательных функций для работы со строками. Использование этих функций вполне возможно, но нужно понимать, что они в первую очередь написаны для нужд фреймворка и дорабатываться специально для приложений не будут.
Утилиты фреймворка
Утилиты фреймворка — это инструменты для сборки и отладки графических элементов фреймворка, создания бинарных файлов для ресурсов системы или приложений, а также инструменты управления фреймворком в отладочных целях. На данный момент существуют две утилиты: генератор бинарных файлов (frmbingen) и отладочный терминал (frmdbgterm). Обе утилиты очень полезны и практически незаменимы.
Генератор бинарных файлов предназначен для сборки бинарных файлов из XML-описаний. Команды генератора принимают исходное текстовое описание, путь к генерируемому файлу и набор параметров сборки. Существуют три команды: две для сборки шрифтов, третья — для сборки ресурсных бинарных файлов, а также бинарных файлов системы и приложений. Шрифт — единственное описание, которое фреймворк не обрабатывает в процессе работы, поэтому необходимо заранее готовить соответствующие бинарные файлы. Сборка шрифтов — продолжительный процесс, выполняющийся отдельно. Другие бинарные файлы могут быть собраны в процессе построения, командой сборки ресурсов make res или в скриптах для сборки приложений. XML-описания системы, приложений, управляющих элементов и ресурсов могут обрабатываться в процессе работы, но соответствующие бинарные файлы обрабатываются существенно быстрее.
Генератор также поддерживает работу со специальными скриптами для пакетной генерации всех нужных файлов. Скрипты могут использовать команды оболочки (cp, mkdir и др.) для формирования файловых структур, копирования собранных файлов и т.п. Это свойство генератора используется процедурой построения фреймворка. Скрипты генератора могут использовать настройки окружения фреймворка и оперировать ими как внутренними переменными (в отличие от переменных окружения оболочки), что обеспечивает высокую приспособляемость скриптов к конфигурации системы.
Отладочный терминал — утилита для коммуникации с приложением-сервером, запущенным в контексте фреймворка (например, приложение debug shell). Утилита отправляет команду, введенную в командной строке, серверу, получает ответ и выводит его в консоли. Терминал имеет и собственные команды, обрабатываемые без обращения к серверу. Также существует набор опциональных параметров, управляющих запуском терминала.
Терминал совместно с приложением debug shell обеспечивает доступ к фреймворку “изнутри”. Debug shell — фоновое системное приложение со всеми привилегиями, используемое для операций, недоступных через пользовательский интерфейс, или для специфических задач (например, выгрузка сервиса, что пока нельзя сделать в SDK). Код приложения в SDK недоступен, так как оно использует заголовочные файлы, отсутствующие в SDK.
Открытая документация
Вся открытая документация по фреймворку содержится на его википедии. Я стараюсь поддерживать её в максимально актуальном состоянии, но всё же документация неполная. В ней явно пока не хватает важных разделов по API фреймворка с детальным его описанием, разделов, посвящённых XML-описаниям системы, списков привилегий, элементов тем, конфигурационных элементов и многих других моментов, полезных для разработчика. Конечный результат может вылиться в целую книжку и даже совсем не тонкую.
По мере возможности я продолжу наполнять вики новыми материалами, наиболее полезными для разработки. Обычно она пополняется при создании новых релизов фреймворка, но, возможно, этот процесс может быть ускорен. Релизы фреймворка выполняются редко, раз в три месяца, быстрее пока не получается. В релиз входят, как правило, крупный функционал или несколько более мелких. Всё это также должно быть отлажено в SDK.
На данный момент на вики находятся следующие статьи:
-
Введение: статья предназначена для описания общих принципов фреймворка и его структуры. В ней рассмотрены все те же пункты, что и в данной публикации, но, может быть, более детально.
-
Установка фреймворка: в ней рассмотрены основные системные требования к установке и использованию SDK и, конечно, шаги установки. Также там есть информация о шагах простройки и обновления компонент фреймворка.
-
Приложения фреймворка: в статье рассматриваются типы приложений фреймворка и их применимость в некоторых случаях.
-
Создание приложений: эта статья представляет собой рабочую инструкцию как создать каркас приложения и как его модифицировать в рамках системы. Она не рассматривает внутреннюю структуру приложений.
-
Структура приложений: этот раздел уже рассматривает внутреннюю структуру каркаса приложения, а также файлы, необходимые для его сборки и интеграции в систему фреймворка. Раздел рассматривает особенности приложений в зависимости от выбранного языка описания, но не содержит рецептов по созданию бизнес-логики приложения (этого пока вообще нет).
-
Примеры приложений: в разделе приведен пользовательский интерфейс приложений фреймворка в виде отдельных скриншотов (если он есть) и краткое описание функционала приложения и его управляющих элементов. Обычно в статье указываются и недостатки данных приложений, которые следует исправить.
-
Структура управляющих элементов: одним из наиболее значимых описаний фреймворка остаются управляющие элементы, которым и посвящена данная статья. В ней рассмотрена общая структура элементов управления на примере некоторых отработанных и широко используемых элементах. Надеюсь, статья дает какое-то понимание, как элементы можно создавать и использовать в приложениях.
-
Утилиты фреймворка: раздел содержит подробное описание утилит фреймворка и описание файлов, которые утилиты используют. Подробно я тут останавливаться не буду.
На вики предполагается добавление статей по мере их наработки и нужности, и в зависимости от запросов целевой аудитории. Чем шире будет обратная связь, тем лучше будет и документация.
Дальнейшее развитие фреймворка
В настоящее время фреймворк находится в стадии активной разработки. Выполняются доработки операционного ядра, добавляются новые встроенные виджеты и совершенствуются уже существующие. Создаются новые API и приложения для их проверки и доработки. Планируются и новые функционалы. Задач много, с большим избытком.
Основным и приоритетным направлением является разработка и совершенствование виджетов. Почти все встроенные виджеты имеют те или иные недостатки и недоработки. Обычно это мало влияет на функционал приложений, но при их разработке встречаются случаи, когда такие недоработки блокируют желаемый функционал. Часто есть обходные пути, но и виджеты тоже дорабатываются. Большинство таких недостатков мне известно, на них заведены задачи в баг-трекере.
В операционном ядре тоже есть недостатки: самые важные из них связаны с неоптимальной обработкой по времени или по расходу оперативной памяти. С ними сложнее, чем с недостатками отдельных виджетов: ошибки при исправлении могут привести к полной неработоспособности фреймворка или еще больше ухудшить ситуацию с оптимизацией. Каждый такой фикс требует оценки рисков как в настоящем, так и в будущем. Чаще встречаются недостатки, связанные с функциональностью отдельных менеджеров — отсутствие информации в структурах, недоработки API-функций, отсутствие непредусмотренных ранее функционалов и другие. Обычно такие дефекты исправляются быстро, по мере обнаружения. Очень редко, но встречаются и дефекты стабильности ядра. Это блокеры, исправление готовится немедленно. Это не относится к дефектам, когда ядро выгружается из-за неправильных XML-описаний — крэш в таких случаях неприятен, он по возможности исправляется, но чаще всего исправляется механизм обработки ошибок в XML, пропустивший ошибку.
Разработка приложений и новых API ведется постоянно, но с меньшим приоритетом. Таких задач много, и они обычно не планируются заранее, выполняются по мере возникновения. Есть и ряд дефектов приложений, но им уделяется мало времени (если это дефект именно приложения, а не фреймворка). Пока нет возможности этим заняться. Насчет графики приложений: графический дизайнер из меня почти никакой, поэтому недостатков графики много, часто она некрасивая или просто топорная.
Новый запланированный функционал — менеджер шрифтов и обработчик жестов. Есть и другие задумки, но о них я пока говорить не буду, они требует дальнейшего обдумывания, анализа сложности, прототипирования и тому подобное. В комментариях я отвечу на соответствующие вопросы, если таковые появятся.
Разумеется, я очень хотел бы, чтобы фреймворк вышел из стадии разработки SDK и был бы реально использован в продуктовом варианте, на конкретных устройствах. Дискуссии на эту тему вполне возможны. Фреймворк уже сейчас является очень большой разработкой для одного человека, поэтому варианты расширения обсуждаются и приветствуются.
На этом у меня, пожалуй, всё. Спасибо, что дочитали до конца. Буду ждать комментариев.
ссылка на оригинал статьи https://habr.com/ru/articles/1067450/