Skip to content

Latest commit

 

History

History
341 lines (219 loc) · 21 KB

File metadata and controls

341 lines (219 loc) · 21 KB

KEmulator bridge specifications

Начиная с версии 2.20, параллельно SWT интерфейсу поставляется "bridge" фронтенд. В этом режиме вместо создания графического окна KEmulator создаёт/подключается к ряду разделяемых объектов, запрашивая ввод пользователя из них и отсылая результаты работы приложения.

Этот документ пытается описать порядок взаимодействия с ним внешних программ.

Warning

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

Почему "bridge", а не "headless"?

С учётом текущего состояния кодовой базы кемулятора, а так же полагания на "десктопные" библиотеки по типу javax.sound, lwjgl, в полностью "безголовом" режиме работа не представляется возможной. Кемулятор всё равно должен быть запущен рядом с рабочим звуковым, графическим серверами и потенциально около-десктопным окружением (открытие vlc ссылок). Фронтенд-мост лишь позволяет избавиться от SWT окна, принимая ввод из внешней программы и отправляя ей отрисованые кадры. Одним из применений, тем не менее, очевидно является запуск в безголовых окружениях, например рядом с weston с --backend=headless и pulseaudio с выходом в RTP.

Системные требования

Bridge-фронт полагается на POSIX механизмы FIFO-файлов и разделяемой памяти. Теоретически, это позволяет работать на любых POSIX системах где возможно запустить кемулятор (в том числе где невозможна работа SWT), в т.ч. на Android и WSL1.

На данный момент налаживается работа на gnu/glibc/linux/systemd системах. После объявления фронта готовым будет отдельно отлажена работа на Android.

Портирование под Windows NT достаточно тривиально (заменить именованые файлы-пайпы на именованые \\.\-пайпы), но вне целей данного проекта.

Далее по тексту "фронтенд" будет относиться не к кемулятору, а к внешней программе, подключающейся к кемулятору. Кемулятор будет упоминаться как бекенд (в свою очередь состоящий из bridge-фронтенда и работающего мидлета-"бекенда", детали чего далее ни мне ни читателю уже не интересны)

Порядок запуска

Необходимо передать в командную строку параметр -bridge <FOLDER>, где <FOLDER> - каталог файловой системы в котором будут распологаться разделяемые объекты.

Предполагается задействование ФС /dev/shmem (на современных ядрах linux, обычно, /dev/shm). Рекомендуемый каталог по умолчанию - /dev/shm/kemulator/bridge/. До запуска в этом каталоге уже должны быть созданы FIFO с именами inputpipe, statepipe, readypipe и readpipe. При запуске, KEmulator проверит их существование и завершит работу при неудаче. Затем будет создан файл screen. Затем KEmulator будет открывать на чтение или запись FIFO в порядке, в котором они перечисленны выше. После успешного подключения ко всем четырём FIFO будет запущен мидлет.

Далее, при упоминании имён файлов без пояснений, предполагается что они располагаются в указанной папке.

Порядок завершения работы

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

Коды выхода

При невозможности завершить ту или иную операцию KEmulator немедлено завершит работу с ненулевым кодом.

  • 21: Не обнаружен один из FIFO
  • 22: Сбой подключения к FIFO
  • 23: зарезервирован.
  • 24: Сбой создания файла-фреймбуфера
  • 25: Сбой записи в statepipe
  • 26: Сбой записи в readypipe
  • 27: Неожиданное закрытие readpipe
  • 28: Сбой записи в buffer
  • 29: Сбой чтения из inputpipe (примечание: фронтенд имеет право отключиться от inputpipe в любой момент, это штатная ситуация, вызывающая завершение работы кемулятора с кодом 0. Аварийной ситуацией является лишь ошибка чтения при подключенном фронтенде)
  • 30: Попытка запуска без мидлета

Входящие события

Входящие события читаются из FIFO с именем inputpipe. Этот FIFO должен быть создан внешним фронтендом.

Формат данных: набор параметров через пробел (0x20), команду завершает UNIX-перевод строки (\n). Данные передаются в кодировке ASCII (только первые 128 символов).

Параметры могут быть:

  • заглавными латинскими символами (T, U, D, Q и т.п.), далее - SYM
  • целыми числами записанными в виде текста (при необходимости отправить число 292 необходимо послать не 0x00000124, а 0x323932), далее - NUM
  • логическим значением, 1 или 0, далее - BOOL

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

Клавиатурный ввод

Тип события Дополнительные параметры
K 2

Параметр 1 - SYM: состояние клавиши.

  • D - была зажата
  • R - "повторение"
  • U - была отпущена

Параметр 2 - NUM: код клавиши.

Пример: K D -6 - зажатие ЛСК (в раскладке S60).

Позиционный ввод

Тип события Дополнительные параметры
T 4

Параметр 1 - NUM: номер указателя. Для мыши 0. Для мультитача - по порядковому номеру начиная с 0.

Параметр 2 - SYM: состояние указателя.

  • D - был зажат
  • M - перемещение
  • U - был отпущен

Параметр 3 - NUM: X.

Параметр 4 - NUM: Y.

Пример: T 0 D 120 119 - зажатие левой кнопки мыши над пикселем с координатами 120:119.

Ввод "дополнительной" LCDUI команды

Тип события Дополнительные параметры
C 1

Параметр 1 - NUM: идентификатор команды. Был принят фронтендом в сообщении с запросом открытия меню.

Пример: C 1296000 - активация команды с номером 1296000.

Может быть отправлено только после запроса от кемулятора.

Для отправки "основной" команды необходимо вызывать клавиатурный ввод для нужной софт-клавиши.

Выдача разрешения

Тип события Дополнительные параметры
P 2

Параметр 1 - NUM: идентификатор разрешения. Был принят фронтендом в сообщении с запросом.

Параметр 2 - BOOL: выдача/отказ.

Пример: P 1296000 1 - выдача разрешения с номером 1296000.

Может быть отправлено только после запроса от кемулятора.

Ввод текста в редактируемую область

Тип события Дополнительные параметры
E 1

WIP

Установка режима X-Ray

Тип события Дополнительные параметры
X 1

Параметр 1 - BOOL: статус.

Пример: X 1 - включение X-Ray режима.

Завершение работы

Для завершения работы фронтенд должен отключиться от FIFO.

Исходящие события

Исходящие события будут записываться в FIFO с именем statepipe. Этот FIFO должен быть создан внешним фронтендом. Данные передаются пакетами. Все числа передаются в порядке BigEndian. Формат данных одного пакета:

  • один байт, тип пакета. Заглавный латинский символ в ASCII. Ниже указано слово/фраза, из которой бралась буква, непосредственно символ-тип выделен.
  • четыре байта, длина пакета N
  • N байт, данные пакета

Пакеты никак не разделяются. Содержимое данных задаётся типом, уникально для каждого типа.

Системные сообщения

Тип пакета: Log

Данные: UTF-8 строка сообщения.

Изменение "видимых" команд

Тип пакета: Commands

Данные: пакет содержит один байт значения 0x00. Слева от него UTF-8 текст левой команды, справа - правой. Любая (или даже обе) из строк может быть нулевой длины. Даже если обе строки пусты, 0x00-байт должен быть передан.

Вызвать команды фронтенд может, передав клавиатурный ввод соответствующей софт-клавиши.

Запрос на открытие меню команд

Тип пакета: Menu

Данные: первые 4 байта пакета - целое число, количество команд. С 5 байта последовательно без разделителей передаются команды в указаном количестве.

Формат каждой команды:

  • 4 байта целого числа - идентификатор команды.
  • 1 байт: символ в ASCII 0, 1 или 2 - тип команды. 0 - обычная, 1 - радиокнопка, 2 - выбранная радиокнопка.
  • N байт до байта 0x00 - текст команды.
  • Один нулевой байт (окончание текста).

Запрос на закрытие меню команд

Тип пакета: clOse menu

Данные: ноль байт.

Вибрация

Тип пакета: Vibra

Данные: восемь байт беззнакового целого: количество миллисекунд длительности вибрации.

Изменение заголовка активного экрана

Тип пакета: Title

Данные: UTF-8 строка заголовка.

Установка области ввода текста.

Некоторые виды MIDP экранов могут содержать области для ввода текста, например, lcdui TextField.

Тип пакета: text Area

Для отмены области: пакет без данных.

Для установки области:

  • 2 байта целого числа - X позиция левого края области
  • 2 байта целого числа - Y позиция верхнего края области
  • 2 байта целого числа - ширина области
  • 2 байта целого числа - высота области
  • До конца пакета: UTF-8 строка текущего текста, может быть пустой.

Координаты передаются в пространстве фреймбуфера.

Установка текущего экрана

В настоящий момент кемулятор различает три типа экранов: TextBox, List и экран-буфер.

Тип пакета: Displayable

Первый байт данных:

  • Экран-буфер: B. Дополнительных данных нет.
  • TextBox: T. Со второго байта до окончания пакета - UTF-8 строка, текст, содержащийся на экране.
  • List: L. Со второго байта до окончания пакета - содержимое экрана. Формат данных WIP.

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

Запрос разрешения

Тип пакета: Permission

Данные: четыре байта целого числа - идентификатор. С пятого байта UTF-8 строка сообщения.

Открытие браузера

Тип пакета: Url

Данные: UTF-8 строка URL-адреса.

Иконка мидлета

Тип пакета: I

Данные:

  • Два байта целого числа: ширина изображения.
  • Два байта целого числа: высота изображения.
  • До конца пакета: RGBA8888 данные изображения.

Видеовывод

В настоящий момент кемулятор различает три типа экранов: TextBox, List и экран-буфер. Информация о первых двух отправляется через statepipe, последний использует фреймбуфер в разделяемой памяти, работа которого описана ниже.

Для фреймбуфера используется одиночный буфер. После обнаружения FIFO входящих событий и до его открытия KEmulator создаст файл screen. В него будут записываться RGBA8888 данные кадра.

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

Для блокировки фреймбуфера используются FIFO readypipe (бек -> фронт) и readpipe (фронт -> бек). Они должны быть созданы фронтендом.

Блокировка буфера

В один момент времени лишь одна из сторон имеет право чтения/записи в буфер. Смена владельца происходит по инициативе стороны, работающей с буфером. Для смены владельца сторона отправляет в исходящее FIFO один байт 0x0A (\n). Перед работой с буфером в будущем сначала необходимо дождаться поступления байта 0x0A во входящем FIFO.

Изначально владельцем буфера является бекенд.

Поведение бэкенда (KEmulator'а)

  1. Сразу после старта бэкенд уже владеет буфером.
  2. При поступлении кадра кадр записывается в буфер начиная с нулевой позиции.
  3. Бэкенд отправляет \n через readypipe фронтенду.
  4. Бэкенд ожидает поступление следующего кадра.
  5. При поступлении кадра бэкенд пытается блокирующе считать один символ из readpipe и ожидает до успеха.
  6. Кадр записывается в буфер.
  7. Переход на шаг 3.

Поведение фронтенда

  1. Фронтенд пытается блокирующе считать один символ из readypipe и ожидает до успеха.
  2. Содержимое буфера обрабатывается фронтендом (вывод на экран, копирование, передача в кодировщик).
  3. Фронтенд отправляет \n через readpipe бэкенду.
  4. Переход на шаг 1.

Рекомендации по реализации

  • С файлами буферов необходимо работать при помощи механизма vmap (java.nio.MappedByteBuffer, System.IO.MemoryMappedFiles.MemoryMappedFile)
  • Файлы буферов необходимо размещать в файловой системе, хранящей своё состояние в оперативной памяти (tmpfs: /dev/shm, /tmp)
  • С FIFO можно работать стандартной IO библиотекой: java.io.FileInputStream, java.io.FileOutputStream, System.IO.File.OpenRead, System.IO.File.OpenWrite
  • Цикл приёма кадров не должен останавливаться, пока активен TextBox или List.
  • Фронтенд должен минимизировать время от получения блокировки до её освобождения настолько насколько возможно.