mdview2: фоновая сборка второго набора кодировки в паузах между клавишами; версия v1.0 (b3)

- индексатор порезан на резюмируемые шаги index_begin/index_step/index_finish;
  межшаговое состояние в статиках модуля, в docset_t не входит
- bg_build_start/bg_step: второй набор (UTF-8 при 8-битном первичном и
  наоборот) строится в idle главного цикла; холдаун после клавиш, спиннер
  погашен (g_bg_building) — фон незаметен
- F8 до готовности докручивает начатое фоном (ветка resume в build_doc),
  а не строит заново; общий setup вынесен в doc_setup
- кодировка в статус-баре показывается сразу (детект/F8), не дожидаясь
  конца индексации
- побочный фикс: UTF-конвертация впереди проверки останова — >4КБ абзац
  больше не обрывает конвертацию остатка
- README.md (новый, v1.0 b3), CHANGELOG.md; дискета: README/DEMO/CHANGES
  в трёх кодировках (пути автодетекта)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 19:33:34 +03:00
parent ae23d2dea2
commit 46553f4e07
8 changed files with 484 additions and 89 deletions
+125 -16
View File
@@ -203,10 +203,12 @@ static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под
static uint8_t g_hex_last = VIEW_MD; /* вид, из которого вошли в HEX (возврат по F4) */
#endif
#endif
uint8_t g_loading; /* extern: 1 во время index_lines() */
uint8_t g_loading; /* extern: 1 во время видимой индексации (build_doc) */
static uint8_t g_first_drawn; /* первый экран уже нарисован */
uint8_t g_f8_enabled; /* extern: можно ли сейчас переключать кодировку */
static uint8_t g_utf_failed; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */
uint8_t g_bg_building; /* extern: 1 во время фонового index_step — гасит спиннер индексатора */
static uint8_t g_bg_doc = 0xFF; /* слот, строящийся в фоне (bg_step); 0xFF = фоновой сборки нет */
uint8_t g_abort; /* extern: F10/Esc во время загрузки → прервать индексацию */
static uint16_t g_last_prog; /* n_lines на последнем обновлении статуса */
@@ -768,26 +770,34 @@ void progress_tick(void)
* Точка входа
* ================================================================== */
/* Строит набор `slot` кооперативно (index_lines + progress_tick).
/* Строит набор `slot` кооперативно (index_step + progress_tick).
* visible=1 — первичный (показываемый): прогрессивная отрисовка первого экрана
* и скролл по готовым строкам. visible=0 — вторичный: index_lines строит его в
* живых глобалах, а progress_tick через свап показывает/листает уже готовый
* и скролл по готовым строкам. visible=0 — вторичный: индексация идёт в живых
* глобалах, а progress_tick через свап показывает/листает уже готовый
* g_disp_doc. Возврат: 0 — успех, <0 — нехватка EMM (-5/-6/-7).
*
* Если slot прямо сейчас строится ФОНОМ (g_bg_doc, см. bg_step ниже) —
* не начинаем заново, а ДОКРУЧИВАЕМ: загружаем частично построенный набор
* и продолжаем с сохранённой позиции индексатора (его межшаговое состояние
* живёт в статиках модуля index и переживает свапы наборов).
*
* UTF-8 набор конвертируется ИНКРЕМЕНТАЛЬНО: выделяем страницы, переключаем
* живой буфер на utf и запускаем конвертацию первого чанка; дальше index_lines
* живой буфер на utf и запускаем конвертацию первого чанка; дальше index_step
* докручивает её впереди позиции чтения (g_utf_building). Конвертер читает
* оригинал из orig_file_phys[] через cv_read (не через fb, который теперь
* указывает на utf). */
static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
/* Настраивает живые глобалы под сборку набора slot: файловый буфер (utf/orig),
* индекс + директория кэша, сброс индексатора. Общая часть build_doc и
* bg_build_start. Возврат: 0 — успех, <0 — нехватка EMM (-5/-6/-7). */
static int doc_setup(uint8_t slot)
{
if (slot == DOC_UTF8) {
if (!utf_avail) { /* первый вход: выделить страницы */
if (!utf_alloc())
if (!utf_alloc())
return -7; /* выделяет страницы + сбрасывает конвертер */
utf_avail = 1;
}
file_blk = utf_blk;
file_blk = utf_blk;
file_pages = utf_pages;
memcpy(file_phys, utf_phys, MAX_PAGES);
if (g_utf_building) {
@@ -798,29 +808,45 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
file_size = utf_size;
}
} else {
file_blk = orig_file_blk;
file_pages = orig_file_pages;
file_blk = orig_file_blk;
file_pages = orig_file_pages;
file_size = orig_file_size;
memcpy(file_phys, orig_file_phys, MAX_PAGES);
}
cur_page = 0xFF;
int rc = alloc_set_storage();
if (rc < 0)
if (rc < 0)
return rc;
index_begin();
return 0;
}
static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
{
if (g_bg_doc == slot) { /* фон уже строит его — продолжить */
doc_load_live(slot);
g_bg_doc = 0xFF;
} else {
int rc = doc_setup(slot);
if (rc < 0)
return rc;
}
set_encoding(enc);
top_line = 0;
top_line = 0;
viewport_x = 0;
g_active_doc = slot;
if (visible) {
g_disp_doc = slot;
g_first_drawn = 0;
if (visible) {
g_disp_doc = slot;
g_first_drawn = 0;
status_encoding(); /* показать кодировку сразу (детект/F8), не дожидаясь индексации */
cur_page = 0xFF; /* BIOS-вывод мог сбить W3-маппинг, на который опирается fb() */
}
g_loading = 1;
g_last_prog = 0;
index_lines();
while (!index_step(255)) {}
g_loading = 0;
/* Зафиксировать конвертацию (на случай обрыва индекса по max_lines до конца
@@ -835,6 +861,69 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
return 0;
}
/* ==================================================================
* Фоновая сборка второго набора (idle-time, между клавишами)
* ==================================================================
* Пока пользователь читает документ, главный цикл в паузах между клавишами
* докручивает индексацию второго набора (UTF-8 при 8-битном первичном и
* наоборот) порциями index_step: свап на bg-набор → несколько итераций →
* свап обратно. Экран при этом не трогается (g_bg_building гасит спиннер),
* задержка реакции на клавишу — не больше одного шага. По готовности F8
* переключает наборы мгновенно; F8 ДО готовности докручивает сборку видимо
* (ветка resume в build_doc), не начиная её заново. */
#define BG_STEP_BUDGET 2u /* итераций индексатора на idle-шаг: латентность клавиши
* = длительность шага (~единицы мс на обычном абзаце) */
#define BG_KEY_HOLDOFF 300u /* пустых опросов kbhit после клавиши до возобновления
* фона — при автоповторе (скролл) фон не вклинивается */
/* Готовит фоновую сборку второго набора: настраивает живые глобалы (буфер,
* индекс, кэш, index_begin) так же, как build_doc, но БЕЗ индексации, и
* возвращает живым прежний набор. При нехватке EMM фон просто не стартует —
* остаётся сегодняшний ленивый путь по F8. */
static void bg_build_start(void)
{
uint8_t slot = (uint8_t)((g_active_doc == DOC_8BIT) ? DOC_UTF8 : DOC_8BIT);
uint8_t vis = g_active_doc;
if (g_doc[slot].built || (slot == DOC_UTF8 && g_utf_failed))
return; /* строить нечего */
doc_save_live(vis);
if (doc_setup(slot) < 0) { /* нет EMM — как и в ленивом пути */
if (slot == DOC_UTF8) g_utf_failed = 1;
doc_load_live(vis);
return;
}
top_line = 0;
viewport_x = 0;
g_active_doc = slot; /* живые глобалы = bg-набор... */
doc_switch(vis); /* ...сохранить его и вернуть видимый */
g_bg_doc = slot;
}
/* Один idle-шаг фона: свап на строящийся набор, порция индексации, свап
* обратно. Межшаговое состояние индексатора/UTF-конвертера — статики их
* модулей, обработчики клавиш видимого набора их не трогают. По завершении
* финализирует набор (дальше F8 переключается мгновенно). */
static void bg_step(void)
{
uint8_t vis = g_active_doc;
doc_switch(g_bg_doc);
g_bg_building = 1;
uint8_t done = index_step(BG_STEP_BUDGET);
g_bg_building = 0;
if (done) {
if (g_utf_building) { /* как build_doc: обрыв индекса до конца исходника */
utf_size = file_size;
g_utf_building = 0;
}
g_doc[g_bg_doc].built = 1; /* doc_save_live поле built не трогает */
g_bg_doc = 0xFF;
}
doc_switch(vis);
}
/* Пропорциональный перенос байт-позиции между буферами разного размера
* (8bit ↔ UTF-8 при F8): off * to / from БЕЗ __mullong/__divulong. Один
* цикл restoring-деления: бит k дроби off/from (сравнение с from/2^k)
@@ -1079,7 +1168,27 @@ int main(int argc, char **argv)
render_full_status();
draw_viewport_from_cache();
/* Второй набор (UTF-8 при 8-битном первичном и наоборот) готовим фоном
* в паузах между клавишами — к первому F8 он обычно уже построен. */
bg_build_start();
for (;;) {
/* Idle-фон: пока второй набор не достроен, вместо блокирующего getkey
* опрашиваем клавиатуру и тратим паузы на bg_step. Холдаун после
* каждой клавиши: при активном скролле (автоповтор) фон молчит и
* плавность не страдает. После достройки — снова блокирующий getkey. */
if (g_bg_doc != 0xFF) {
uint16_t idle = 0;
while (!kbhit()) {
if (idle < BG_KEY_HOLDOFF) {
idle++;
continue;
}
bg_step();
if (g_bg_doc == 0xFF)
break;
}
}
uint16_t k = getkey();
uint8_t ascii = (uint8_t)(k & 0xFF);
uint8_t scan = (uint8_t)((k >> 8) & 0x7F);