Volkov: добавить Sprinter Commander

Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места.

Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
This commit is contained in:
2026-09-10 10:45:30 +03:00
parent 05bcd8197e
commit 8e389c03f8
870 changed files with 21310 additions and 25 deletions
@@ -0,0 +1,324 @@
# Sprinter Commander: клавиши и команды
Статус: PoC и первые команды 0.2 подтверждены в MAME
Связанные документы:
[требования](commander-requirements.md),
[архитектура](commander-architecture.md),
[план разработки](commander-roadmap.md)
## 1. Назначение
Документ отделяет физические клавиши Sprinter от логических команд
Commander. Только модуль `sc_input.c` знает scan-коды, а только
`sc_command.c` знает таблицу их назначения.
Панели и операции получают значения `ScCommand`, а не результаты `getkey()`.
Это позволяет позднее добавить мышь, переназначение клавиш и альтернативную
клавиатуру без изменений файловых функций.
## 2. Формат события
```c
enum {
SC_MOD_SHIFT = 0x01,
SC_MOD_CTRL = 0x02,
SC_MOD_ALT = 0x04
};
typedef struct {
uint8_t ascii;
uint8_t scan;
uint8_t modifiers;
} ScKeyEvent;
```
Правила нормализации:
- обычный символ записывается в `ascii`;
- расширенная клавиша записывается в `scan`, `ascii == 0`;
- при активном Ctrl/Alt/Shift DSS может установить бит 7 scan-кода;
диагностическая трассировка показывает raw-значение, а `sc_input`
передаёт командам базовый `scan & 0x7F`;
- левый и правый Shift объединяются в `SC_MOD_SHIFT`;
- левый и правый Ctrl объединяются в `SC_MOD_CTRL`;
- левый и правый Alt объединяются в `SC_MOD_ALT`;
- CapsLock, NumLock, ScrollLock, Insert и Rus/Lat не являются модификаторами
команды;
- управляющий ASCII-код имеет приоритет над live-состоянием модификатора;
- неизвестная комбинация преобразуется в `SC_CMD_NONE`.
## 3. Подтверждённые расширенные scan-коды
Значения уже объявлены в `<conio.h>`:
| Клавиша | Scan-код | Макрос |
|---|---:|---|
| F1 | `0x3B` | `KEY_F1` |
| F2 | `0x3C` | `KEY_F2` |
| F3 | `0x3D` | `KEY_F3` |
| F4 | `0x3E` | `KEY_F4` |
| F5 | `0x3F` | `KEY_F5` |
| F6 | `0x40` | `KEY_F6` |
| F7 | `0x41` | `KEY_F7` |
| F8 | `0x42` | `KEY_F8` |
| F9 | `0x43` | `KEY_F9` |
| F10 | `0x44` | `KEY_F10` |
| End | `0x51` | `KEY_END` |
| Down | `0x52` | `KEY_DOWN` |
| PgDn | `0x53` | `KEY_PGDN` |
| Left | `0x54` | `KEY_LEFT` |
| Delete | `0x55` | `KEY_DEL` |
| Right | `0x56` | `KEY_RIGHT` |
| Home | `0x57` | `KEY_HOME` |
| Up | `0x58` | `KEY_UP` |
| PgUp | `0x59` | `KEY_PGUP` |
| Insert | `0x50` | `KEY_INS` |
Все значения таблицы, включая `Insert` и `Delete`, подтверждены P1-пробой в
MAME. `F11` и `F12` в P1 не проверялись и остаются неподтверждёнными до
появления команды, которая их использует.
Фактическая последовательность MAME для навигации была:
`Up=58`, `Down=52`, `Left=54`, `Right=56`, `Home=57`, `End=51`,
`PgUp=59`, `PgDn=53`, `Insert=50`, `Delete=55`. F1–F10 дали непрерывный
диапазон `3B..44`.
## 4. Логические команды
Численные значения enum не являются стабильным ABI до версии 0.2.
```c
typedef enum {
SC_CMD_NONE = 0,
SC_CMD_CURSOR_UP,
SC_CMD_CURSOR_DOWN,
SC_CMD_CURSOR_LEFT,
SC_CMD_CURSOR_RIGHT,
SC_CMD_PAGE_UP,
SC_CMD_PAGE_DOWN,
SC_CMD_HOME,
SC_CMD_END,
SC_CMD_PANEL_SWITCH,
SC_CMD_OPEN,
SC_CMD_PARENT,
SC_CMD_ROOT,
SC_CMD_REFRESH,
SC_CMD_CANCEL,
SC_CMD_SELECT,
SC_CMD_SELECT_MASK,
SC_CMD_UNSELECT_MASK,
SC_CMD_INVERT_SELECTION,
SC_CMD_HELP,
SC_CMD_USER_MENU,
SC_CMD_VIEW,
SC_CMD_EDIT,
SC_CMD_COPY,
SC_CMD_MOVE_RENAME,
SC_CMD_MKDIR,
SC_CMD_DELETE,
SC_CMD_MAIN_MENU,
SC_CMD_QUIT,
SC_CMD_LEFT_DRIVE,
SC_CMD_RIGHT_DRIVE,
SC_CMD_SWAP_PANELS,
SC_CMD_TOGGLE_SHELL,
SC_CMD_QUICK_VIEW,
SC_CMD_INFO_PANEL,
SC_CMD_FIND,
SC_CMD_TREE
} ScCommand;
```
Дополнительные команды вводятся при появлении соответствующей функции, а не
заранее в обработчиках-заглушках.
## 5. Клавиши PoC 0.1
### 5.1. Навигация панели
| Клавиша | Команда | Поведение PoC |
|---|---|---|
| `Up` | `SC_CMD_CURSOR_UP` | На одну запись вверх |
| `Down` | `SC_CMD_CURSOR_DOWN` | На одну запись вниз |
| `PgUp` | `SC_CMD_PAGE_UP` | На 27 записей вверх |
| `PgDn` | `SC_CMD_PAGE_DOWN` | На 27 записей вниз |
| `Home` | `SC_CMD_HOME` | Первая запись |
| `End` | `SC_CMD_END` | Последняя запись |
| `Left` | `SC_CMD_NONE` | Не используется в одноколоночной панели PoC |
| `Right` | `SC_CMD_NONE` | Не используется в одноколоночной панели PoC |
| `Tab` | `SC_CMD_PANEL_SWITCH` | Переключить активную панель |
| `Enter` | `SC_CMD_OPEN` | Войти в каталог или запустить `.EXE` |
| `Ctrl+PgUp` | `SC_CMD_PARENT` | Перейти в родительский каталог |
| `Backspace` | `SC_CMD_PARENT` | То же, пока командной строки нет |
| `Ctrl+R` | `SC_CMD_REFRESH` | Перечитать активную панель |
| `Ctrl+F3` | `SC_CMD_SORT_NAME` | Имя; повторное нажатие меняет направление |
| `Ctrl+F4` | `SC_CMD_SORT_EXTENSION` | Расширение; повтор меняет направление |
| `Ctrl+F5` | `SC_CMD_SORT_DATE` | Дата/время; повтор меняет направление |
| `Ctrl+F6` | `SC_CMD_SORT_SIZE` | Размер; повтор меняет направление |
| `Insert` | `SC_CMD_SELECT` | Выбрать/снять текущую запись и перейти вниз |
| `*` | `SC_CMD_INVERT_SELECTION` | Инвертировать только файлы панели; каталоги не менятся |
`SC_CMD_CURSOR_LEFT/RIGHT` сохраняются в enum для будущего многоколоночного
режима, но PoC их не генерирует.
### 5.2. Функциональные клавиши PoC
| Клавиша | Команда | Состояние |
|---|---|---|
| `F1` | `SC_CMD_HELP` | Зарезервирована, неактивна |
| `F2` | `SC_CMD_USER_MENU` | Зарезервирована, неактивна |
| `F3` | `SC_CMD_VIEW` | Зарезервирована, неактивна |
| `F4` | `SC_CMD_EDIT` | Зарезервирована, неактивна |
| `F5` | `SC_CMD_COPY` | Один файл/каталог либо группа выбранных файлов и directory roots |
| `F6` | `SC_CMD_MOVE_RENAME` | Локальный rename одной записи; move/group позже в 0.2 |
| `F7` | `SC_CMD_MKDIR` | Создать каталог; 0.2, выполнено |
| `F8` | `SC_CMD_DELETE` | Одна запись либо выбранная группа; каталоги группы удаляются рекурсивно после одного подтверждения |
| `F9` | `SC_CMD_MAIN_MENU` | Зарезервирована, неактивна |
| `F10` | `SC_CMD_QUIT` | Открыть подтверждение штатного выхода |
PoC не должен выполнять разрушительное действие по неактивной клавише.
### 5.3. Общие клавиши PoC
| Клавиша | Команда | Контекст |
|---|---|---|
| `Esc` | `SC_CMD_CANCEL` | Закрыть сообщение или отменить job |
| `Esc` | `SC_CMD_NONE` | Обычный режим панелей |
| `Enter` | подтвердить | Диалог сообщения |
| `Y`/`N` | да/нет | Диалог подтверждения, если он появится |
## 6. Контекстная обработка
Одна клавиша может иметь разное действие в зависимости от режима. Приоритет
обработчиков фиксирован:
1. Активная аварийная ошибка.
2. Модальный диалог.
3. Активный job.
4. Командная строка или полноэкранный pseudo-shell версии 1.0.
5. Активная панель.
6. Глобальные команды приложения.
Примеры:
- Во время копирования `Esc` запрашивает отмену, но `F10` не завершает
процесс немедленно.
- В диалоге `Enter` подтверждает кнопку, а не открывает файл под курсором.
- После закрытия диалога то же событие не передаётся панели повторно.
- `F10` во время job игнорируется с подсказкой отменять операцию через `Esc`;
подтверждение выхода открывается только после завершения или отмены job.
- В режиме панелей печатный символ передаётся однострочному редактору;
`Enter` выполняет непустую строку, а при пустой строке открывает запись.
- В pseudo-shell `Up/Down` листают историю команд, а не панель; `Ctrl+O`
возвращает панели с сохранёнными путями и курсорами.
- `Esc` сначала очищает непустую команду, затем закрывает полноэкранный режим;
событие не передаётся следующему контексту повторно.
## 7. Нормализация управляющих ASCII-кодов
На части клавиатур `Ctrl+буква` может приходить как ASCII `0x010x1A` без
надёжного сохранения состояния Ctrl. Поэтому ввод должен распознавать
следующие значения независимо от `kbd_mod_state()`:
| ASCII | Комбинация | Команда |
|---:|---|---|
| `0x12` | `Ctrl+R` | `SC_CMD_REFRESH` |
| `0x15` | `Ctrl+U` | `SC_CMD_SWAP_PANELS`, после PoC |
| `0x0F` | `Ctrl+O` | `SC_CMD_TOGGLE_SHELL`, обязательно к 1.0 |
| `0x11` | `Ctrl+Q` | `SC_CMD_QUICK_VIEW`, после PoC |
| `0x0C` | `Ctrl+L` | `SC_CMD_INFO_PANEL`, после PoC |
`Tab` (`0x09`), `Enter` (`0x0D`), `Esc` (`0x1B`) и `Backspace` (`0x08`)
обрабатываются как обычные управляющие ASCII-клавиши.
P1 в MAME показал второй, фактически основной для текущего DSS путь:
| Комбинация | ASCII | Raw scan | Base scan | `kbd_mod_state()` | Compact |
|---|---:|---:|---:|---:|---:|
| `Ctrl+R` | `00` | `93` | `13` | `0028` | `02` |
| `Ctrl+PgUp` | `00` | `D9` | `59` | `0028` | `02` |
| `Alt+F1` | `00` | `BB` | `3B` | `0014` | `04` |
| `Esc` | `1B` | `01` | `01` | `0000` | `00` |
Таким образом, `sc_input` обязан поддерживать оба представления: сначала
управляющий ASCII-код, затем пару `(base scan, modifiers)`. Проверять raw
scan `93/D9/BB` прямо в таблице команд нельзя: базовый scan с модификатором
получается снятием бита 7. Скриншоты пробы находятся в
[`artifacts/mame-p1/keyboard-commander`](../artifacts/mame-p1/keyboard-commander/sprinter/0003.png).
## 8. Предварительная карта полной версии
Эта таблица резервирует привычные сочетания VC, но не является требованием
PoC.
| Клавиша | Команда | Планируемая версия |
|---|---|---:|
| `Insert` | Выделить/снять выделение и перейти вниз | 0.2, выполнено |
| `Gray +` или `+` | Выделить по маске | 0.2, обычный `+` выполнен |
| `Gray -` или `-` | Снять выделение по маске | 0.2, обычный `-` выполнен |
| `Gray *` или `*` | Инвертировать выделение | 0.2, `*` выполнено |
| `F1` | Помощь | 0.4 |
| `F2` | Пользовательское меню | 0.4 |
| `F3` | Просмотр | 0.4 |
| `F4` | Редактор | 0.4 |
| `F5` | Копирование | 0.1/0.2 |
| `F6` | Перемещение/переименование | 0.2, локальный rename выполнен |
| `F7` | Создать каталог | 0.2, выполнено |
| `F8` | Удалить | 0.2, single и recursive group выполнены |
| `F9` | Главное меню | 0.4 |
| `F10` | Выход | 0.1 |
| `Alt+F1` | Диск левой панели | 0.4 |
| `Alt+F2` | Диск правой панели | 0.4 |
| `Alt+F7` | Поиск файлов | 0.6 |
| `Alt+F10` | Дерево каталогов | 0.6 |
| `Ctrl+R` | Перечитать панель | 0.1 |
| `Ctrl+F3..F6` | Name/ext/date/size, повтор меняет направление | 0.2 |
| `Ctrl+U` | Обменять панели местами | 0.4 |
| `Ctrl+O` | Панели / интерактивный полноэкранный pseudo-shell | 0.4/1.0 |
| `Ctrl+PgUp` | Родительский каталог | 0.1 |
| `Ctrl+\` | Корень текущего диска | 0.2 |
| `Ctrl+Q` | Quick view | 0.6 |
| `Ctrl+L` | Информационная панель | 0.6 |
Shift-варианты функциональных клавиш не резервируются до появления
конкретной необходимости. Это предотвращает преждевременное копирование всей
таблицы VC без соответствующей функции.
## 9. Нижняя строка клавиш
Строка 31 содержит десять сегментов шириной по восемь символов. В PoC:
```text
1 Help 2 Menu 3 View 4 Edit 5 Copy 6 Ren 7 Mkdir 8 Delete9 Menu 10 Quit
```
Это логическая схема, а не точная строка байтов. Названия сокращаются до
доступной ширины. Поддерживаемые действия (`F5`, `F10`) рисуются активным
цветом, остальные — неактивным.
После добавления модификатора строка клавиш может временно показывать
назначения `Alt`/`Ctrl`, но это не входит в PoC.
## 10. Проверки клавиатуры P1/P2
В P1 на MAME подтверждены F1–F10, восемь навигационных клавиш,
Insert/Delete, Esc, Ctrl+R, Ctrl+PgUp и Alt+F1. Отдельная проба также
подтвердила compact-состояния Shift=`01`, Ctrl=`02`, Alt=`04`.
В P2 остаются поведенческие проверки уже внутри UI:
1. `Tab`, `Enter` и `Backspace` во всех контекстах.
2. Одновременное удержание нескольких модификаторов.
3. CapsLock, NumLock и Rus/Lat.
4. Отсутствие повторного выполнения события после закрытия диалога.
5. Keyboard repeat на границах списка.
6. Приём `Esc` во время копирования большого файла.
7. Повтор P1-набора на реальном Sprinter.
Любое расхождение с таблицей сначала оформляется отдельным репро и только
затем учитывается в Commander.