Начиная с версии 2.20, параллельно SWT интерфейсу поставляется "bridge" фронтенд. В этом режиме вместо создания графического окна KEmulator создаёт/подключается к ряду разделяемых объектов, запрашивая ввод пользователя из них и отсылая результаты работы приложения.
Этот документ пытается описать порядок взаимодействия с ним внешних программ.
Warning
Протокол будет дорабатываться в будущем, включая ломающие изменения. Версионирование на данный момент не предусмотрено из-за единственного существующего "сервера" и максимум двух планируемых "клиентов". Если вы хотите реализовать этот протокол в своём проекте, пожалуйста, сначала спросите в русской ветке чата/сервера nnproject о моих планах на эту спецификацию.
С учётом текущего состояния кодовой базы кемулятора, а так же полагания на "десктопные" библиотеки по типу
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.
| Тип события | Дополнительные параметры |
|---|---|
C |
1 |
Параметр 1 - NUM: идентификатор команды. Был принят фронтендом в сообщении с запросом открытия меню.
Пример:
C 1296000- активация команды с номером 1296000.
Может быть отправлено только после запроса от кемулятора.
Для отправки "основной" команды необходимо вызывать клавиатурный ввод для нужной софт-клавиши.
| Тип события | Дополнительные параметры |
|---|---|
P |
2 |
Параметр 1 - NUM: идентификатор разрешения. Был принят фронтендом в сообщении с запросом.
Параметр 2 - BOOL: выдача/отказ.
Пример:
P 1296000 1- выдача разрешения с номером 1296000.
Может быть отправлено только после запроса от кемулятора.
| Тип события | Дополнительные параметры |
|---|---|
E |
1 |
WIP
| Тип события | Дополнительные параметры |
|---|---|
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.
Изначально владельцем буфера является бекенд.
- Сразу после старта бэкенд уже владеет буфером.
- При поступлении кадра кадр записывается в буфер начиная с нулевой позиции.
- Бэкенд отправляет
\nчерезreadypipeфронтенду. - Бэкенд ожидает поступление следующего кадра.
- При поступлении кадра бэкенд пытается блокирующе считать один символ из
readpipeи ожидает до успеха. - Кадр записывается в буфер.
- Переход на шаг 3.
- Фронтенд пытается блокирующе считать один символ из
readypipeи ожидает до успеха. - Содержимое буфера обрабатывается фронтендом (вывод на экран, копирование, передача в кодировщик).
- Фронтенд отправляет
\nчерезreadpipeбэкенду. - Переход на шаг 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.
- Фронтенд должен минимизировать время от получения блокировки до её освобождения настолько насколько возможно.